Правки сериализации, persistence и api-sandbox
This commit is contained in:
parent
9e5b70161d
commit
f882cf4c3a
|
|
@ -5,21 +5,96 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>API Debug Console</title>
|
<title>API Debug Console</title>
|
||||||
<style>
|
<style>
|
||||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
body {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #ffffff;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
.left-panel {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
border-right: 1px solid #ffffff;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.right-panel {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
h1 { color: #ffffff; border-bottom: 1px solid #ffffff; padding-bottom: 10px; margin-top: 0; }
|
||||||
.form-group { margin: 10px 0; }
|
.form-group { margin: 10px 0; }
|
||||||
label { display: block; margin-bottom: 5px; }
|
label { display: block; margin-bottom: 5px; color: #ffffff; }
|
||||||
select, input { padding: 8px; width: 100%; max-width: 300px; }
|
select, input, textarea {
|
||||||
button { padding: 10px 20px; background: #007bff; color: white; border: none; cursor: pointer; }
|
padding: 8px;
|
||||||
button:hover { background: #0056b3; }
|
width: 100%;
|
||||||
#result { margin-top: 20px; white-space: pre-wrap; background: #f8f9fa; padding: 10px; border-radius: 4px; }
|
max-width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #ffffff;
|
||||||
|
color: #ffffff;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
select option { background: #1a1a1a; color: #ffffff; }
|
||||||
|
button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
background: transparent;
|
||||||
|
color: #ffffff;
|
||||||
|
border: 1px solid #ffffff;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
background: #ffffff;
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
#result, #request {
|
||||||
|
margin-top: 20px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
background: transparent;
|
||||||
|
padding: 10px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
.params { margin-top: 10px; }
|
.params { margin-top: 10px; }
|
||||||
.param { margin-bottom: 10px; }
|
.param { margin-bottom: 15px; }
|
||||||
|
.param-header { display: flex; align-items: center; gap: 10px; margin-bottom: 5px; }
|
||||||
|
.checkbox-wrapper { display: flex; align-items: center; gap: 5px; }
|
||||||
|
.checkbox-wrapper input[type="checkbox"] { width: auto; margin: 0; accent-color: #ffffff; }
|
||||||
|
.checkbox-wrapper label { margin: 0; font-weight: normal; white-space: nowrap; color: #ffffff; }
|
||||||
|
input:disabled, textarea:disabled {
|
||||||
|
background: transparent;
|
||||||
|
color: #666;
|
||||||
|
border-color: #666;
|
||||||
|
}
|
||||||
|
input:focus, select:focus, textarea:focus {
|
||||||
|
border-color: #ffffff;
|
||||||
|
box-shadow: 0 0 2px #ffffff;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 80px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="left-panel">
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<h1>API Debug Console</h1>
|
<h1>API Debug Console</h1>
|
||||||
|
|
||||||
|
<div id="user-info" class="form-group" style="margin-bottom: 20px;">
|
||||||
|
<label>Пользователь:</label>
|
||||||
|
<span id="username" style="color: #ccc;">не авторизован</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="service">Сервис:</label>
|
<label for="service">Сервис:</label>
|
||||||
<select id="service"></select>
|
<select id="service"></select>
|
||||||
|
|
@ -33,8 +108,16 @@
|
||||||
<div id="params-container" class="params"></div>
|
<div id="params-container" class="params"></div>
|
||||||
|
|
||||||
<button id="send-btn">Отправить</button>
|
<button id="send-btn">Отправить</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="right-panel">
|
||||||
|
<h1>Result</h1>
|
||||||
<div id="result"></div>
|
<div id="result"></div>
|
||||||
|
|
||||||
|
<h1 style="margin-top: 30px;">Request</h1>
|
||||||
|
<div id="request"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/main.js"></script>
|
<script type="module" src="/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,40 @@
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
|
|
||||||
let apiSpec = null
|
let apiSpec = null
|
||||||
|
let currentUser = null
|
||||||
|
|
||||||
|
async function loadUserProfile() {
|
||||||
|
try {
|
||||||
|
const requestData = {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
method: 'Profile.get',
|
||||||
|
params: {},
|
||||||
|
id: Date.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch('http://localhost:8080/api', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
credentials: 'include',
|
||||||
|
body: JSON.stringify(requestData)
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
if (result.result) {
|
||||||
|
currentUser = result.result
|
||||||
|
$('#username').text(currentUser.name || 'неизвестный пользователь')
|
||||||
|
} else {
|
||||||
|
currentUser = null
|
||||||
|
$('#username').text('не авторизован')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка загрузки профиля:', error)
|
||||||
|
currentUser = null
|
||||||
|
$('#username').text('не авторизован')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadApiSpec() {
|
async function loadApiSpec() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -58,14 +92,50 @@ function createParamInputs(methodName, serviceName) {
|
||||||
if (!method || !method.params) return
|
if (!method || !method.params) return
|
||||||
|
|
||||||
method.params.forEach(param => {
|
method.params.forEach(param => {
|
||||||
const required = !param.optional ? ' (обязательно)' : ''
|
const required = !param.optional ? ' (обязательно)' : ' (необязательно)'
|
||||||
|
const isOptional = param.optional
|
||||||
|
|
||||||
|
const isObjectOrArray = param.type === 'object' || param.type === 'array'
|
||||||
|
const defaultValue = param.type === 'object' ? '{}' : (param.type === 'array' ? '[]' : '')
|
||||||
|
const inputElement = isObjectOrArray
|
||||||
|
? `<textarea id="param-${param.name}" data-param="${param.name}" placeholder="${param.description}" rows="4" style="font-family: 'Courier New', monospace;">${defaultValue}</textarea>`
|
||||||
|
: `<input type="text" id="param-${param.name}" data-param="${param.name}" placeholder="${param.description}">`
|
||||||
|
|
||||||
|
if (isOptional) {
|
||||||
const $paramDiv = $(`
|
const $paramDiv = $(`
|
||||||
<div class="param">
|
<div class="param">
|
||||||
|
<div class="param-header">
|
||||||
<label for="param-${param.name}">${param.name}${required} (${param.type}):</label>
|
<label for="param-${param.name}">${param.name}${required} (${param.type}):</label>
|
||||||
<input type="text" id="param-${param.name}" data-param="${param.name}" placeholder="${param.description}">
|
<div class="checkbox-wrapper">
|
||||||
|
<input type="checkbox" id="defined-${param.name}" data-param="${param.name}" class="defined-checkbox">
|
||||||
|
<label for="defined-${param.name}">is defined</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${inputElement}
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
$paramsContainer.append($paramDiv)
|
$paramsContainer.append($paramDiv)
|
||||||
|
|
||||||
|
$(`#defined-${param.name}`).on('change', function() {
|
||||||
|
const $input = $(`#param-${param.name}`)
|
||||||
|
const isChecked = $(this).is(':checked')
|
||||||
|
$input.prop('disabled', !isChecked)
|
||||||
|
if (!isChecked) {
|
||||||
|
$input.val(isObjectOrArray ? defaultValue : '')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Initially disable optional fields
|
||||||
|
$(`#param-${param.name}`).prop('disabled', true)
|
||||||
|
} else {
|
||||||
|
const $paramDiv = $(`
|
||||||
|
<div class="param">
|
||||||
|
<label for="param-${param.name}">${param.name}${required} (${param.type}):</label>
|
||||||
|
${inputElement}
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
$paramsContainer.append($paramDiv)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,14 +149,57 @@ async function sendRequest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = {}
|
const params = {}
|
||||||
$('.param input').each(function() {
|
let parseError = null
|
||||||
const paramName = $(this).data('param')
|
|
||||||
const value = $(this).val()
|
$('.param').each(function () {
|
||||||
if (value.trim() !== '') {
|
const $checkbox = $(this).find('.defined-checkbox')
|
||||||
|
const $input = $(this).find('input[type="text"], textarea')
|
||||||
|
const paramName = $input.data('param')
|
||||||
|
const paramType = $input.attr('id').replace('param-', '')
|
||||||
|
|
||||||
|
// Find parameter type from method definition
|
||||||
|
const service = apiSpec[serviceName]
|
||||||
|
const method = service.methods.find(m => m.name === methodName)
|
||||||
|
const paramDef = method.params.find(p => p.name === paramName)
|
||||||
|
const isObjectOrArray = paramDef && (paramDef.type === 'object' || paramDef.type === 'array')
|
||||||
|
|
||||||
|
if ($checkbox.length > 0) {
|
||||||
|
// Optional parameter
|
||||||
|
if ($checkbox.is(':checked') && $input.val().trim() !== '') {
|
||||||
|
const value = $input.val().trim()
|
||||||
|
if (isObjectOrArray) {
|
||||||
|
try {
|
||||||
|
params[paramName] = JSON.parse(value)
|
||||||
|
} catch (e) {
|
||||||
|
parseError = `Ошибка парсинга JSON для поля "${paramName}": ${e.message}`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
params[paramName] = value
|
params[paramName] = value
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Required parameter
|
||||||
|
const value = $input.val().trim()
|
||||||
|
if (value !== '') {
|
||||||
|
if (isObjectOrArray) {
|
||||||
|
try {
|
||||||
|
params[paramName] = JSON.parse(value)
|
||||||
|
} catch (e) {
|
||||||
|
parseError = `Ошибка парсинга JSON для поля "${paramName}": ${e.message}`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
params[paramName] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (parseError) {
|
||||||
|
$('#result').text(parseError)
|
||||||
|
$('#request').text(JSON.stringify(requestData, null, 2))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const requestData = {
|
const requestData = {
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
method: `${serviceName}.${methodName}`,
|
method: `${serviceName}.${methodName}`,
|
||||||
|
|
@ -95,20 +208,32 @@ async function sendRequest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#result').text('Отправка запроса...')
|
$('#result').text('Отправка запроса...')
|
||||||
|
$('#request').text('')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api', {
|
const response = await fetch('http://localhost:8080/api', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
|
credentials: 'include',
|
||||||
body: JSON.stringify(requestData)
|
body: JSON.stringify(requestData)
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await response.json()
|
const result = await response.json()
|
||||||
$('#result').text(JSON.stringify(result, null, 2))
|
$('#result').text(JSON.stringify(result, null, 2))
|
||||||
|
$('#request').text(JSON.stringify(requestData, null, 2))
|
||||||
|
|
||||||
|
// Проверяем, нужно ли обновить профиль пользователя
|
||||||
|
if (result.result && (
|
||||||
|
methodName.startsWith('authenticateBy') ||
|
||||||
|
methodName === 'logout'
|
||||||
|
)) {
|
||||||
|
await loadUserProfile()
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
$('#result').text(`Ошибка: ${error.message}`)
|
$('#result').text(`Ошибка: ${error.message}`)
|
||||||
|
$('#request').text(JSON.stringify(requestData, null, 2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,6 +242,9 @@ $(document).ready(async () => {
|
||||||
if (apiSpec) {
|
if (apiSpec) {
|
||||||
populateServices()
|
populateServices()
|
||||||
|
|
||||||
|
// Автоматически загружаем профиль пользователя
|
||||||
|
await loadUserProfile()
|
||||||
|
|
||||||
$('#service').on('change', function () {
|
$('#service').on('change', function () {
|
||||||
const serviceName = $(this).val()
|
const serviceName = $(this).val()
|
||||||
populateMethods(serviceName)
|
populateMethods(serviceName)
|
||||||
|
|
|
||||||
|
|
@ -116,11 +116,11 @@ public class SpecGenerator {
|
||||||
if (type == void.class || type == Void.class) return "void";
|
if (type == void.class || type == Void.class) return "void";
|
||||||
|
|
||||||
if (type.isArray() || Collection.class.isAssignableFrom(type) || type == ArrayNode.class) {
|
if (type.isArray() || Collection.class.isAssignableFrom(type) || type == ArrayNode.class) {
|
||||||
return "Array";
|
return "array";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JsonNode.class.isAssignableFrom(type)) {
|
if (JsonNode.class.isAssignableFrom(type)) {
|
||||||
return "Object";
|
return "object";
|
||||||
}
|
}
|
||||||
|
|
||||||
return type.getSimpleName();
|
return type.getSimpleName();
|
||||||
|
|
|
||||||
147
api.spec.json
147
api.spec.json
|
|
@ -1,23 +1,86 @@
|
||||||
{
|
{
|
||||||
"Auth" : {
|
"Profile" : {
|
||||||
"name" : "Auth",
|
"name" : "Profile",
|
||||||
"methods" : [ {
|
"methods" : [ {
|
||||||
"name" : "generateToken",
|
"name" : "get",
|
||||||
"description" : "Generates a new API token and returns its details",
|
"description" : "Get current user profile",
|
||||||
"return" : "Object",
|
"return" : "void",
|
||||||
|
"accessLevel" : "Guest",
|
||||||
|
"params" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "save",
|
||||||
|
"description" : "edit current user profile",
|
||||||
|
"return" : "boolean",
|
||||||
"accessLevel" : "User",
|
"accessLevel" : "User",
|
||||||
"params" : [ {
|
"params" : [ {
|
||||||
"name" : "permanent",
|
"name" : "login",
|
||||||
"type" : "boolean",
|
"type" : "string",
|
||||||
"description" : "If true, creates a token that never expires",
|
"description" : "User login. Have to be unique",
|
||||||
"optional" : true
|
"optional" : false
|
||||||
}, {
|
}, {
|
||||||
"name" : "name",
|
"name" : "name",
|
||||||
"type" : "string",
|
"type" : "string",
|
||||||
"description" : "Display name for the token. If not provided, the User-Agent header will be used",
|
"description" : "User display name",
|
||||||
|
"optional" : false
|
||||||
|
}, {
|
||||||
|
"name" : "password",
|
||||||
|
"type" : "string",
|
||||||
|
"description" : "Change user password if defined",
|
||||||
"optional" : true
|
"optional" : true
|
||||||
|
}, {
|
||||||
|
"name" : "values",
|
||||||
|
"type" : "object",
|
||||||
|
"description" : "User custom values",
|
||||||
|
"optional" : false
|
||||||
|
} ]
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"UserManagement" : {
|
||||||
|
"name" : "UserManagement",
|
||||||
|
"methods" : [ {
|
||||||
|
"name" : "getById",
|
||||||
|
"description" : "Load user object by ID",
|
||||||
|
"return" : "object",
|
||||||
|
"accessLevel" : "Admin",
|
||||||
|
"params" : [ {
|
||||||
|
"name" : "id",
|
||||||
|
"type" : "number",
|
||||||
|
"description" : "User id",
|
||||||
|
"optional" : false
|
||||||
} ]
|
} ]
|
||||||
}, {
|
}, {
|
||||||
|
"name" : "remove",
|
||||||
|
"description" : "Remove user by ID",
|
||||||
|
"return" : "object",
|
||||||
|
"accessLevel" : "Admin",
|
||||||
|
"params" : [ {
|
||||||
|
"name" : "id",
|
||||||
|
"type" : "number",
|
||||||
|
"description" : "User id",
|
||||||
|
"optional" : false
|
||||||
|
} ]
|
||||||
|
}, {
|
||||||
|
"name" : "save",
|
||||||
|
"description" : "Get all users as list",
|
||||||
|
"return" : "array",
|
||||||
|
"accessLevel" : "Admin",
|
||||||
|
"params" : [ {
|
||||||
|
"name" : "user",
|
||||||
|
"type" : "object",
|
||||||
|
"description" : "User object to save",
|
||||||
|
"optional" : false
|
||||||
|
} ]
|
||||||
|
}, {
|
||||||
|
"name" : "getAll",
|
||||||
|
"description" : "Get all users as list",
|
||||||
|
"return" : "array",
|
||||||
|
"accessLevel" : "Admin",
|
||||||
|
"params" : [ ]
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"Auth" : {
|
||||||
|
"name" : "Auth",
|
||||||
|
"methods" : [ {
|
||||||
"name" : "authenticateByPassword",
|
"name" : "authenticateByPassword",
|
||||||
"description" : "Authenticates a user using login and password. Returns true if authentication is successful.",
|
"description" : "Authenticates a user using login and password. Returns true if authentication is successful.",
|
||||||
"return" : "boolean",
|
"return" : "boolean",
|
||||||
|
|
@ -34,17 +97,27 @@
|
||||||
"optional" : false
|
"optional" : false
|
||||||
} ]
|
} ]
|
||||||
}, {
|
}, {
|
||||||
"name" : "logout",
|
"name" : "getTokens",
|
||||||
"description" : "Logs out the current user. Returns true if logout is successful, false if the user is not logged in",
|
"description" : "Retrieves all API tokens associated with the current user",
|
||||||
"return" : "boolean",
|
"return" : "array",
|
||||||
"accessLevel" : "User",
|
"accessLevel" : "User",
|
||||||
"params" : [ ]
|
"params" : [ ]
|
||||||
}, {
|
}, {
|
||||||
"name" : "getTokens",
|
"name" : "generateToken",
|
||||||
"description" : "Retrieves all API tokens associated with the current user",
|
"description" : "Generates a new API token and returns its details",
|
||||||
"return" : "Array",
|
"return" : "object",
|
||||||
"accessLevel" : "User",
|
"accessLevel" : "User",
|
||||||
"params" : [ ]
|
"params" : [ {
|
||||||
|
"name" : "permanent",
|
||||||
|
"type" : "boolean",
|
||||||
|
"description" : "If true, creates a token that never expires",
|
||||||
|
"optional" : true
|
||||||
|
}, {
|
||||||
|
"name" : "name",
|
||||||
|
"type" : "string",
|
||||||
|
"description" : "Display name for the token. If not provided, the User-Agent header will be used",
|
||||||
|
"optional" : true
|
||||||
|
} ]
|
||||||
}, {
|
}, {
|
||||||
"name" : "authenticateByToken",
|
"name" : "authenticateByToken",
|
||||||
"description" : "Authenticates a user using an API token. Returns true if authentication is successful.",
|
"description" : "Authenticates a user using an API token. Returns true if authentication is successful.",
|
||||||
|
|
@ -56,44 +129,12 @@
|
||||||
"description" : "API token string for authentication",
|
"description" : "API token string for authentication",
|
||||||
"optional" : false
|
"optional" : false
|
||||||
} ]
|
} ]
|
||||||
} ]
|
}, {
|
||||||
},
|
"name" : "logout",
|
||||||
"Profile" : {
|
"description" : "Logs out the current user. Returns true if logout is successful, false if the user is not logged in",
|
||||||
"name" : "Profile",
|
"return" : "boolean",
|
||||||
"methods" : [ {
|
|
||||||
"name" : "get",
|
|
||||||
"description" : "",
|
|
||||||
"return" : "void",
|
|
||||||
"accessLevel" : "User",
|
"accessLevel" : "User",
|
||||||
"params" : [ ]
|
"params" : [ ]
|
||||||
}, {
|
|
||||||
"name" : "save",
|
|
||||||
"description" : "",
|
|
||||||
"return" : "void",
|
|
||||||
"accessLevel" : "User",
|
|
||||||
"params" : [ ]
|
|
||||||
} ]
|
|
||||||
},
|
|
||||||
"UserManagement" : {
|
|
||||||
"name" : "UserManagement",
|
|
||||||
"methods" : [ {
|
|
||||||
"name" : "save",
|
|
||||||
"description" : "",
|
|
||||||
"return" : "void",
|
|
||||||
"accessLevel" : "Admin",
|
|
||||||
"params" : [ ]
|
|
||||||
}, {
|
|
||||||
"name" : "getAll",
|
|
||||||
"description" : "",
|
|
||||||
"return" : "void",
|
|
||||||
"accessLevel" : "Admin",
|
|
||||||
"params" : [ ]
|
|
||||||
}, {
|
|
||||||
"name" : "getById",
|
|
||||||
"description" : "",
|
|
||||||
"return" : "void",
|
|
||||||
"accessLevel" : "Admin",
|
|
||||||
"params" : [ ]
|
|
||||||
} ]
|
} ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -18,4 +18,5 @@ public interface RepositoryService extends Service {
|
||||||
|
|
||||||
ObjectMapper getMapper();
|
ObjectMapper getMapper();
|
||||||
|
|
||||||
|
Class<? extends PersistenceEntity> getEntityTypeByName(String entityName);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,18 @@ import ru.kirillius.XCP.Commons.Service;
|
||||||
import ru.kirillius.XCP.Logging.Logger;
|
import ru.kirillius.XCP.Logging.Logger;
|
||||||
import ru.kirillius.XCP.Logging.LoggingSystem;
|
import ru.kirillius.XCP.Logging.LoggingSystem;
|
||||||
import ru.kirillius.XCP.Logging.LoggingSystemImpl;
|
import ru.kirillius.XCP.Logging.LoggingSystemImpl;
|
||||||
|
import ru.kirillius.XCP.Persistence.PersistenceEntity;
|
||||||
|
import ru.kirillius.XCP.Persistence.Repository;
|
||||||
import ru.kirillius.XCP.Persistence.RepositoryServiceImpl;
|
import ru.kirillius.XCP.Persistence.RepositoryServiceImpl;
|
||||||
import ru.kirillius.XCP.Security.ConfigManagerImpl;
|
import ru.kirillius.XCP.Security.ConfigManagerImpl;
|
||||||
import ru.kirillius.XCP.Security.SecurityManager;
|
import ru.kirillius.XCP.Security.SecurityManager;
|
||||||
import ru.kirillius.XCP.Security.SecurityManagerImpl;
|
import ru.kirillius.XCP.Security.SecurityManagerImpl;
|
||||||
|
import ru.kirillius.XCP.Services.RepositoryService;
|
||||||
import ru.kirillius.XCP.Services.ServiceLoadPriority;
|
import ru.kirillius.XCP.Services.ServiceLoadPriority;
|
||||||
import ru.kirillius.XCP.Services.WebService;
|
import ru.kirillius.XCP.Services.WebService;
|
||||||
import ru.kirillius.XCP.web.WebServiceImpl;
|
import ru.kirillius.XCP.web.WebServiceImpl;
|
||||||
|
import tools.jackson.databind.node.ArrayNode;
|
||||||
|
import tools.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
@ -85,11 +90,38 @@ public class Application implements Context {
|
||||||
shutdown();
|
shutdown();
|
||||||
throw new RuntimeException("Error loading services");
|
throw new RuntimeException("Error loading services");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkDefaultInstall();
|
||||||
|
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
|
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
|
||||||
|
|
||||||
((WebServiceImpl) getService(WebService.class)).join();
|
((WebServiceImpl) getService(WebService.class)).join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final static String DEFAULT_INSTALLER_FILE = "defaultEntities.json";
|
||||||
|
|
||||||
|
private void checkDefaultInstall() {
|
||||||
|
try (var stream = getClass().getClassLoader().getResourceAsStream(DEFAULT_INSTALLER_FILE)) {
|
||||||
|
var repositoryService = getService(RepositoryService.class);
|
||||||
|
var mapper = repositoryService.getMapper();
|
||||||
|
var array = (ArrayNode) mapper.readTree(stream);
|
||||||
|
for (var node : array) {
|
||||||
|
var entry = (ObjectNode) node;
|
||||||
|
var typeName = entry.get("type").asString();
|
||||||
|
var entityType = repositoryService.getEntityTypeByName(typeName);
|
||||||
|
@SuppressWarnings("unchecked") var repository = (Repository<PersistenceEntity>) repositoryService.getRepositoryForEntity(entityType);
|
||||||
|
var deserialized = repository.deserialize((ObjectNode) entry.get("entity"));
|
||||||
|
if (repository.get(deserialized.getUuid()) == null) {
|
||||||
|
log.warning("Installing default entity " + typeName + " (" + deserialized + ").");
|
||||||
|
repository.save(deserialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Unable to load file " + DEFAULT_INSTALLER_FILE, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void loadServices() {
|
private void loadServices() {
|
||||||
var servicesToLoad = List.of(RepositoryServiceImpl.class, WebServiceImpl.class);
|
var servicesToLoad = List.of(RepositoryServiceImpl.class, WebServiceImpl.class);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "User",
|
||||||
|
"entity": {
|
||||||
|
"login": "admin",
|
||||||
|
"name": "Administrator",
|
||||||
|
"role": "Admin",
|
||||||
|
"values": {},
|
||||||
|
"uuid": "00000000-0000-0000-0000-000000000000",
|
||||||
|
"passwordHash": "$argon2id$v=19$m=65536,t=3,p=1$SBqQtx5adxoG53V0TgqmDw$zIy0Wiq53m9r/SOldtCXWXLWbvZuS0F3HHILxpUsLhQ"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -2,12 +2,9 @@ package ru.kirillius.XCP.Security;
|
||||||
|
|
||||||
import de.mkammerer.argon2.Argon2;
|
import de.mkammerer.argon2.Argon2;
|
||||||
import de.mkammerer.argon2.Argon2Factory;
|
import de.mkammerer.argon2.Argon2Factory;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
public class Argon2HashUtility implements HashUtility {
|
public class Argon2HashUtility implements HashUtility {
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final static HashUtility instance = new Argon2HashUtility();
|
|
||||||
|
|
||||||
private final Argon2 argon2;
|
private final Argon2 argon2;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package ru.kirillius.XCP.Security;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class Argon2HashUtilityTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hash() {
|
||||||
|
var util = new Argon2HashUtility();
|
||||||
|
var hashed = util.hash("admin");
|
||||||
|
assertThat(hashed).isNotNull().isNotEmpty().doesNotContain("admin").isEqualTo("$argon2id$v=19$m=65536,t=3,p=1$SBqQtx5adxoG53V0TgqmDw$zIy0Wiq53m9r/SOldtCXWXLWbvZuS0F3HHILxpUsLhQ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validate() {
|
||||||
|
var util = new Argon2HashUtility();
|
||||||
|
var hashed = util.hash("admin");
|
||||||
|
assertThat(util.validate("admin", hashed)).isTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package ru.kirillius.XCP.Persistence;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@MappedSuperclass
|
||||||
|
public abstract class AbstractEntity implements PersistenceEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@JsonProperty
|
||||||
|
private long id = 0;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@JsonProperty
|
||||||
|
@Column(unique = true, nullable = false, updatable = false)
|
||||||
|
private UUID uuid;
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void prePersist() {
|
||||||
|
if (uuid == null) {
|
||||||
|
uuid = UUID.randomUUID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (o.getClass() != getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var that = (AbstractEntity) o;
|
||||||
|
return Objects.equals(uuid, that.uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -23,16 +23,11 @@ public class EntityReferenceDeserializer extends StdDeserializer<EntityReference
|
||||||
var id = node.get("id").asLong();
|
var id = node.get("id").asLong();
|
||||||
var uuid = node.get("uuid").asString();
|
var uuid = node.get("uuid").asString();
|
||||||
|
|
||||||
try {
|
var repository = repositoryService.getRepositoryForEntity((Class<? extends PersistenceEntity>) repositoryService.getEntityTypeByName(type));
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
var repository = repositoryService.getRepositoryForEntity((Class<? extends PersistenceEntity>) Class.forName(type));
|
|
||||||
if (uuid != null) {
|
if (uuid != null) {
|
||||||
return new EntityReference(repository.get(UUID.fromString(uuid)));
|
return new EntityReference(repository.get(UUID.fromString(uuid)));
|
||||||
}
|
}
|
||||||
return new EntityReference(repository.get(id));
|
return new EntityReference(repository.get(id));
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ public class EntityReferenceSerializer extends StdSerializer<EntityReference> {
|
||||||
}
|
}
|
||||||
gen.writeStartObject();
|
gen.writeStartObject();
|
||||||
var baseType = repositoryService.getEntityBaseType(value.getClass());
|
var baseType = repositoryService.getEntityBaseType(value.getClass());
|
||||||
gen.writeStringProperty("type", baseType.getName());
|
gen.writeStringProperty("type", baseType.getSimpleName());
|
||||||
gen.writeNumberProperty("id", value.getId());
|
gen.writeNumberProperty("id", value.getId());
|
||||||
gen.writeStringProperty("uuid", value.getUuid().toString());
|
gen.writeStringProperty("uuid", value.getUuid().toString());
|
||||||
gen.writeEndObject();
|
gen.writeEndObject();
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,13 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.hibernate.annotations.UuidGenerator;
|
|
||||||
import ru.kirillius.XCP.Commons.StreamHandler;
|
import ru.kirillius.XCP.Commons.StreamHandler;
|
||||||
import ru.kirillius.XCP.Persistence.AbstractRepository;
|
import ru.kirillius.XCP.Persistence.*;
|
||||||
import ru.kirillius.XCP.Persistence.Entities.ApiToken;
|
import ru.kirillius.XCP.Persistence.Entities.ApiToken;
|
||||||
import ru.kirillius.XCP.Persistence.Entities.User;
|
import ru.kirillius.XCP.Persistence.Entities.User;
|
||||||
import ru.kirillius.XCP.Persistence.EntityImplementation;
|
|
||||||
import ru.kirillius.XCP.Persistence.EntityReference;
|
|
||||||
import ru.kirillius.XCP.Persistence.RepositoryServiceImpl;
|
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@EntityImplementation(ApiTokenRepositoryImpl.TokenEntity.class)
|
@EntityImplementation(ApiTokenRepositoryImpl.TokenEntity.class)
|
||||||
public class ApiTokenRepositoryImpl extends AbstractRepository<ApiToken> implements ApiTokenRepository {
|
public class ApiTokenRepositoryImpl extends AbstractRepository<ApiToken> implements ApiTokenRepository {
|
||||||
|
|
@ -37,17 +31,7 @@ public class ApiTokenRepositoryImpl extends AbstractRepository<ApiToken> impleme
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public static class TokenEntity implements ApiToken {
|
public static class TokenEntity extends AbstractEntity implements ApiToken {
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
@JsonProperty
|
|
||||||
private long id = 0;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
@Column(unique = true, nullable = false)
|
|
||||||
@UuidGenerator
|
|
||||||
private UUID uuid;
|
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@Column
|
@Column
|
||||||
|
|
@ -79,15 +63,5 @@ public class ApiTokenRepositoryImpl extends AbstractRepository<ApiToken> impleme
|
||||||
this.user = (UserRepositoryImpl.UserEntity) parent;
|
this.user = (UserRepositoryImpl.UserEntity) parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (!(o instanceof TokenEntity that)) return false;
|
|
||||||
return Objects.equals(uuid, that.uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(uuid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,9 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.hibernate.annotations.UuidGenerator;
|
|
||||||
import ru.kirillius.XCP.Commons.StreamHandler;
|
import ru.kirillius.XCP.Commons.StreamHandler;
|
||||||
import ru.kirillius.XCP.Persistence.Entities.Group;
|
|
||||||
import ru.kirillius.XCP.Persistence.*;
|
import ru.kirillius.XCP.Persistence.*;
|
||||||
|
import ru.kirillius.XCP.Persistence.Entities.Group;
|
||||||
import tools.jackson.databind.node.JsonNodeFactory;
|
import tools.jackson.databind.node.JsonNodeFactory;
|
||||||
import tools.jackson.databind.node.ObjectNode;
|
import tools.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
|
@ -67,8 +66,6 @@ public class GroupRepositoryImpl extends AbstractNodeRepository<Group> implement
|
||||||
super.save(entity);
|
super.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "Groups")
|
@Table(name = "Groups")
|
||||||
@Builder
|
@Builder
|
||||||
|
|
@ -76,24 +73,12 @@ public class GroupRepositoryImpl extends AbstractNodeRepository<Group> implement
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public static class GroupEntity implements Group {
|
public static class GroupEntity extends AbstractEntity implements Group {
|
||||||
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
@JsonProperty
|
|
||||||
private long id = 0;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
@Column(unique = true, nullable = false)
|
|
||||||
@UuidGenerator
|
|
||||||
private UUID uuid;
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private String name = "";
|
private String name = "";
|
||||||
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@Getter
|
@Getter
|
||||||
|
|
@ -140,9 +125,6 @@ public class GroupRepositoryImpl extends AbstractNodeRepository<Group> implement
|
||||||
this.parent = (GroupEntity) parent;
|
this.parent = (GroupEntity) parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTags(TagCollection tags) {
|
public void setTags(TagCollection tags) {
|
||||||
this.tags = tags.stream().map(t -> (TagRepositoryImpl.TagEntity) t).collect(Collectors.toSet());
|
this.tags = tags.stream().map(t -> (TagRepositoryImpl.TagEntity) t).collect(Collectors.toSet());
|
||||||
|
|
@ -163,15 +145,5 @@ public class GroupRepositoryImpl extends AbstractNodeRepository<Group> implement
|
||||||
@ManyToMany(fetch = FetchType.EAGER)
|
@ManyToMany(fetch = FetchType.EAGER)
|
||||||
private Set<TagRepositoryImpl.TagEntity> tags = new HashSet<>();
|
private Set<TagRepositoryImpl.TagEntity> tags = new HashSet<>();
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (!(o instanceof GroupEntity that)) return false;
|
|
||||||
return Objects.equals(uuid, that.uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(uuid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.hibernate.annotations.UuidGenerator;
|
|
||||||
import ru.kirillius.XCP.Data.PollSettings;
|
import ru.kirillius.XCP.Data.PollSettings;
|
||||||
import ru.kirillius.XCP.Data.ValueTransformationChain;
|
import ru.kirillius.XCP.Data.ValueTransformationChain;
|
||||||
|
import ru.kirillius.XCP.Persistence.*;
|
||||||
import ru.kirillius.XCP.Persistence.Entities.Group;
|
import ru.kirillius.XCP.Persistence.Entities.Group;
|
||||||
import ru.kirillius.XCP.Persistence.Entities.Input;
|
import ru.kirillius.XCP.Persistence.Entities.Input;
|
||||||
import ru.kirillius.XCP.Persistence.*;
|
|
||||||
import ru.kirillius.XCP.Serialization.PollSettingsConverter;
|
import ru.kirillius.XCP.Serialization.PollSettingsConverter;
|
||||||
import ru.kirillius.XCP.Serialization.ValueTransformationChainConverter;
|
import ru.kirillius.XCP.Serialization.ValueTransformationChainConverter;
|
||||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
|
@ -17,9 +16,7 @@ import tools.jackson.databind.node.JsonNodeFactory;
|
||||||
import tools.jackson.databind.node.ObjectNode;
|
import tools.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@EntityImplementation(InputRepositoryImpl.InputEntity.class)
|
@EntityImplementation(InputRepositoryImpl.InputEntity.class)
|
||||||
|
|
@ -37,8 +34,6 @@ public class InputRepositoryImpl extends AbstractNodeRepository<Input> implement
|
||||||
super.save(entity);
|
super.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "Inputs")
|
@Table(name = "Inputs")
|
||||||
@Builder
|
@Builder
|
||||||
|
|
@ -46,17 +41,7 @@ public class InputRepositoryImpl extends AbstractNodeRepository<Input> implement
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public static class InputEntity implements Input {
|
public static class InputEntity extends AbstractEntity implements Input {
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
@JsonProperty
|
|
||||||
private long id = 0;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
@Column(unique = true, nullable = false)
|
|
||||||
@UuidGenerator
|
|
||||||
private UUID uuid;
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
|
@ -116,17 +101,6 @@ public class InputRepositoryImpl extends AbstractNodeRepository<Input> implement
|
||||||
@ManyToMany(fetch = FetchType.EAGER)
|
@ManyToMany(fetch = FetchType.EAGER)
|
||||||
private Set<TagRepositoryImpl.TagEntity> tags = new HashSet<>();
|
private Set<TagRepositoryImpl.TagEntity> tags = new HashSet<>();
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (!(o instanceof InputEntity that)) return false;
|
|
||||||
return Objects.equals(uuid, that.uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PollSettings getPollSettings() {
|
public PollSettings getPollSettings() {
|
||||||
return pollSettings;
|
return pollSettings;
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.hibernate.annotations.UuidGenerator;
|
|
||||||
import ru.kirillius.XCP.Data.ValueTransformationChain;
|
import ru.kirillius.XCP.Data.ValueTransformationChain;
|
||||||
|
import ru.kirillius.XCP.Persistence.*;
|
||||||
import ru.kirillius.XCP.Persistence.Entities.Group;
|
import ru.kirillius.XCP.Persistence.Entities.Group;
|
||||||
import ru.kirillius.XCP.Persistence.Entities.Output;
|
import ru.kirillius.XCP.Persistence.Entities.Output;
|
||||||
import ru.kirillius.XCP.Persistence.*;
|
|
||||||
import ru.kirillius.XCP.Serialization.ValueTransformationChainConverter;
|
import ru.kirillius.XCP.Serialization.ValueTransformationChainConverter;
|
||||||
import tools.jackson.databind.node.JsonNodeFactory;
|
import tools.jackson.databind.node.JsonNodeFactory;
|
||||||
import tools.jackson.databind.node.ObjectNode;
|
import tools.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@EntityImplementation(OutputRepositoryImpl.OutputEntity.class)
|
@EntityImplementation(OutputRepositoryImpl.OutputEntity.class)
|
||||||
|
|
@ -34,8 +31,6 @@ public class OutputRepositoryImpl extends AbstractNodeRepository<Output> impleme
|
||||||
super.save(entity);
|
super.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "Outputs")
|
@Table(name = "Outputs")
|
||||||
@Builder
|
@Builder
|
||||||
|
|
@ -43,16 +38,7 @@ public class OutputRepositoryImpl extends AbstractNodeRepository<Output> impleme
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public static class OutputEntity implements Output {
|
public static class OutputEntity extends AbstractEntity implements Output {
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
@JsonProperty
|
|
||||||
private long id = 0;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
@Column(unique = true, nullable = false)
|
|
||||||
@UuidGenerator
|
|
||||||
private UUID uuid;
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
|
@ -112,18 +98,6 @@ public class OutputRepositoryImpl extends AbstractNodeRepository<Output> impleme
|
||||||
@ManyToMany(fetch = FetchType.EAGER)
|
@ManyToMany(fetch = FetchType.EAGER)
|
||||||
private Set<TagRepositoryImpl.TagEntity> tags = new HashSet<>();
|
private Set<TagRepositoryImpl.TagEntity> tags = new HashSet<>();
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (!(o instanceof OutputEntity that)) return false;
|
|
||||||
return Objects.equals(uuid, that.uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@Getter
|
@Getter
|
||||||
|
|
@ -136,6 +110,5 @@ public class OutputRepositoryImpl extends AbstractNodeRepository<Output> impleme
|
||||||
@Convert(converter = ValueTransformationChainConverter.class)
|
@Convert(converter = ValueTransformationChainConverter.class)
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private ValueTransformationChain transformationChain = new ValueTransformationChain();
|
private ValueTransformationChain transformationChain = new ValueTransformationChain();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
package ru.kirillius.XCP.Persistence.Repositories;
|
package ru.kirillius.XCP.Persistence.Repositories;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.hibernate.annotations.UuidGenerator;
|
|
||||||
import ru.kirillius.XCP.Commons.ResourceHandler;
|
import ru.kirillius.XCP.Commons.ResourceHandler;
|
||||||
import ru.kirillius.XCP.Persistence.*;
|
import ru.kirillius.XCP.Persistence.*;
|
||||||
import ru.kirillius.XCP.Persistence.Entities.Tag;
|
import ru.kirillius.XCP.Persistence.Entities.Tag;
|
||||||
|
|
@ -11,8 +12,6 @@ import ru.kirillius.XCP.Persistence.Entities.Tag;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
|
@ -50,8 +49,6 @@ public class TagRepositoryImpl extends AbstractRepository<Tag> implements TagRep
|
||||||
throw new RuntimeException("Unable to find tags by names " + names, e);
|
throw new RuntimeException("Unable to find tags by names " + names, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (tags.size() != names.size()) {
|
if (tags.size() != names.size()) {
|
||||||
var foundNames = tags.stream().map(Tag::getName).toList();
|
var foundNames = tags.stream().map(Tag::getName).toList();
|
||||||
names.forEach(tagName -> {
|
names.forEach(tagName -> {
|
||||||
|
|
@ -84,16 +81,7 @@ public class TagRepositoryImpl extends AbstractRepository<Tag> implements TagRep
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public static class TagEntity implements Tag {
|
public static class TagEntity extends AbstractEntity implements Tag {
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
@JsonProperty
|
|
||||||
private long id = 0;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
@Column(unique = true, nullable = false)
|
|
||||||
@UuidGenerator
|
|
||||||
private UUID uuid;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|
@ -126,16 +114,5 @@ public class TagRepositoryImpl extends AbstractRepository<Tag> implements TagRep
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (!(o instanceof TagEntity tagEntity)) return false;
|
|
||||||
return Objects.equals(name, tagEntity.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.hibernate.annotations.UuidGenerator;
|
|
||||||
import ru.kirillius.XCP.Commons.Context;
|
import ru.kirillius.XCP.Commons.Context;
|
||||||
import ru.kirillius.XCP.Persistence.AbstractRepository;
|
import ru.kirillius.XCP.Persistence.*;
|
||||||
import ru.kirillius.XCP.Persistence.ContextReferencedEntity;
|
|
||||||
import ru.kirillius.XCP.Persistence.Entities.User;
|
import ru.kirillius.XCP.Persistence.Entities.User;
|
||||||
import ru.kirillius.XCP.Persistence.EntityImplementation;
|
|
||||||
import ru.kirillius.XCP.Persistence.RepositoryServiceImpl;
|
|
||||||
import ru.kirillius.XCP.Security.UserRole;
|
import ru.kirillius.XCP.Security.UserRole;
|
||||||
import tools.jackson.databind.node.JsonNodeFactory;
|
import tools.jackson.databind.node.JsonNodeFactory;
|
||||||
import tools.jackson.databind.node.ObjectNode;
|
import tools.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@EntityImplementation(UserRepositoryImpl.UserEntity.class)
|
@EntityImplementation(UserRepositoryImpl.UserEntity.class)
|
||||||
public class UserRepositoryImpl extends AbstractRepository<User> implements UserRepository {
|
public class UserRepositoryImpl extends AbstractRepository<User> implements UserRepository {
|
||||||
|
|
@ -42,23 +36,13 @@ public class UserRepositoryImpl extends AbstractRepository<User> implements User
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public static class UserEntity implements User, ContextReferencedEntity {
|
public static class UserEntity extends AbstractEntity implements User, ContextReferencedEntity {
|
||||||
@Transient
|
@Transient
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
@JsonProperty
|
|
||||||
private long id = 0;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
@Column(unique = true, nullable = false)
|
|
||||||
@UuidGenerator
|
|
||||||
private UUID uuid;
|
|
||||||
|
|
||||||
@Column(nullable = false, unique = true)
|
@Column(nullable = false, unique = true)
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private String login = "";
|
private String login = "";
|
||||||
|
|
@ -82,6 +66,14 @@ public class UserRepositoryImpl extends AbstractRepository<User> implements User
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private ObjectNode values = JsonNodeFactory.instance.objectNode();
|
private ObjectNode values = JsonNodeFactory.instance.objectNode();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void prePersist() {
|
||||||
|
super.prePersist();
|
||||||
|
if (login == null || login.isEmpty()) {
|
||||||
|
login = "user" + System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPassword(String password) {
|
public void setPassword(String password) {
|
||||||
passwordHash = context.getSecurityManager().getHashUtility().hash(password);
|
passwordHash = context.getSecurityManager().getHashUtility().hash(password);
|
||||||
|
|
@ -91,16 +83,5 @@ public class UserRepositoryImpl extends AbstractRepository<User> implements User
|
||||||
public boolean verifyPassword(String password) {
|
public boolean verifyPassword(String password) {
|
||||||
return context.getSecurityManager().getHashUtility().validate(password, passwordHash);
|
return context.getSecurityManager().getHashUtility().validate(password, passwordHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (!(o instanceof UserEntity that)) return false;
|
|
||||||
return Objects.equals(uuid, that.uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(uuid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -220,4 +220,9 @@ public final class RepositoryServiceImpl implements RepositoryService {
|
||||||
return repositoryEntityBindings.get(repositoryImplClass);
|
return repositoryEntityBindings.get(repositoryImplClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends PersistenceEntity> getEntityTypeByName(String entityName) {
|
||||||
|
return entityBaseBindings.values().stream().filter(e -> e.getSimpleName().equals(entityName)).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@ class RepositoryServiceImplTest {
|
||||||
@Setter
|
@Setter
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (!(o instanceof EntityImpl entity)) return false;
|
if (!(o instanceof EntityImpl entity)) return false;
|
||||||
|
|
@ -187,4 +186,18 @@ class RepositoryServiceImplTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestGetByName() {
|
||||||
|
try (var service = instantiateTestService(List.of(RepoImpl.class))) {
|
||||||
|
var repository = service.getRepository(TestRepository.class);
|
||||||
|
var testEntity = repository.create();
|
||||||
|
repository.save(List.of(testEntity));
|
||||||
|
|
||||||
|
var typeByName = service.getEntityTypeByName(TestEntity.class.getSimpleName());
|
||||||
|
|
||||||
|
assertThat(typeByName).isNotNull().isEqualTo(TestEntity.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,6 +170,13 @@ public class JsonRpcServlet extends HttpServlet {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JsonRpcResponse createErrorResponse(JsonRpcRequest request, JsonRpcErrorCode code, String message) {
|
||||||
|
return JsonRpcResponse.builder().jsonrpc("2.0")
|
||||||
|
.id(request == null ? -1L : request.getId())
|
||||||
|
.error(new JsonRpcError(code, message))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private JsonRpcResponse processRequest(JsonRpcRequest request, CallContext callContext) {
|
private JsonRpcResponse processRequest(JsonRpcRequest request, CallContext callContext) {
|
||||||
var split = request.getMethod().split(Pattern.quote("."), 2);
|
var split = request.getMethod().split(Pattern.quote("."), 2);
|
||||||
if (split.length != 2) {
|
if (split.length != 2) {
|
||||||
|
|
@ -188,14 +195,15 @@ public class JsonRpcServlet extends HttpServlet {
|
||||||
|
|
||||||
if (callContext.getCurrentUser().getRole().getLevel() < methodBinding.getAccessLevel().getLevel()) {
|
if (callContext.getCurrentUser().getRole().getLevel() < methodBinding.getAccessLevel().getLevel()) {
|
||||||
callContext.getResponse().setStatus(HttpServletResponse.SC_FORBIDDEN);
|
callContext.getResponse().setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
return createErrorResponse(request, JsonRpcErrorCode.INTERNAL_ERROR);
|
log.error("Forbidden JSON-RPC request: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(request));
|
||||||
|
return createErrorResponse(request, JsonRpcErrorCode.INTERNAL_ERROR, "Forbidden due to access level");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var result = methodBinding.getMethod().invoke(methodBinding.getService(), callContext);
|
var result = methodBinding.getMethod().invoke(methodBinding.getService(), callContext);
|
||||||
return JsonRpcResponse.builder().id(request.getId()).result(result).build();
|
return JsonRpcResponse.builder().id(request.getId()).jsonrpc("2.0").result(result).build();
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||||
log.error("Failed to process JSON-RPC request: " + mapper.valueToTree(request).toString(), e);
|
log.error("Failed to process JSON-RPC request: " + mapper.writerWithDefaultPrettyPrinter().valueToTree(request).toString(), e);
|
||||||
return createErrorResponse(request, JsonRpcErrorCode.INVOCATION_ERROR);
|
return createErrorResponse(request, JsonRpcErrorCode.INVOCATION_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,16 @@ import tools.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
public class Profile extends JsonRpcService {
|
public class Profile extends JsonRpcService {
|
||||||
|
|
||||||
@JsonRpcMethod(accessLevel = UserRole.User)
|
@JsonRpcMethod(
|
||||||
|
accessLevel = UserRole.User,
|
||||||
|
description = "edit current user profile",
|
||||||
|
parameters = {
|
||||||
|
@JsonRpcMethod.Parameter(name = "login", description = "User login. Have to be unique", type = String.class),
|
||||||
|
@JsonRpcMethod.Parameter(name = "name", description = "User display name", type = String.class),
|
||||||
|
@JsonRpcMethod.Parameter(name = "password", description = "Change user password if defined", type = String.class, optional = true),
|
||||||
|
@JsonRpcMethod.Parameter(name = "values", description = "User custom values", type = ObjectNode.class)
|
||||||
|
},
|
||||||
|
returnType = boolean.class)
|
||||||
public boolean save(CallContext call) {
|
public boolean save(CallContext call) {
|
||||||
var userRepository = call.getContext().getService(RepositoryService.class).getRepository(UserRepository.class);
|
var userRepository = call.getContext().getService(RepositoryService.class).getRepository(UserRepository.class);
|
||||||
var user = call.getCurrentUser();
|
var user = call.getCurrentUser();
|
||||||
|
|
@ -49,7 +58,10 @@ public class Profile extends JsonRpcService {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonRpcMethod(accessLevel = UserRole.User)
|
@JsonRpcMethod(
|
||||||
|
accessLevel = UserRole.Guest,
|
||||||
|
description = "Get current user profile"
|
||||||
|
)
|
||||||
public ObjectNode get(CallContext call) {
|
public ObjectNode get(CallContext call) {
|
||||||
var userRepository = call.getContext().getService(RepositoryService.class).getRepository(UserRepository.class);
|
var userRepository = call.getContext().getService(RepositoryService.class).getRepository(UserRepository.class);
|
||||||
var user = call.getCurrentUser();
|
var user = call.getCurrentUser();
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,14 @@ import tools.jackson.databind.node.ObjectNode;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class UserManagement extends JsonRpcService {
|
public class UserManagement extends JsonRpcService {
|
||||||
@JsonRpcMethod(accessLevel = UserRole.Admin)
|
@JsonRpcMethod(
|
||||||
|
accessLevel = UserRole.Admin,
|
||||||
|
description = "Get all users as list",
|
||||||
|
parameters = {
|
||||||
|
|
||||||
|
},
|
||||||
|
returnType = ArrayNode.class
|
||||||
|
)
|
||||||
public ArrayNode getAll(CallContext call) throws IOException {
|
public ArrayNode getAll(CallContext call) throws IOException {
|
||||||
var userRepository = call.getContext().getService(RepositoryService.class).getRepository(UserRepository.class);
|
var userRepository = call.getContext().getService(RepositoryService.class).getRepository(UserRepository.class);
|
||||||
try (var handler = userRepository.getAll()) {
|
try (var handler = userRepository.getAll()) {
|
||||||
|
|
@ -21,18 +28,51 @@ public class UserManagement extends JsonRpcService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonRpcMethod(accessLevel = UserRole.Admin)
|
@JsonRpcMethod(
|
||||||
|
accessLevel = UserRole.Admin,
|
||||||
|
description = "Get all users as list",
|
||||||
|
parameters = {
|
||||||
|
@JsonRpcMethod.Parameter(name = "user", description = "User object to save", type = ObjectNode.class)
|
||||||
|
},
|
||||||
|
returnType = ArrayNode.class
|
||||||
|
)
|
||||||
public void save(CallContext call) {
|
public void save(CallContext call) {
|
||||||
var userRepository = call.getContext().getService(RepositoryService.class).getRepository(UserRepository.class);
|
var userRepository = call.getContext().getService(RepositoryService.class).getRepository(UserRepository.class);
|
||||||
var user = userRepository.deserialize((ObjectNode) requireParam(call, "user"));
|
var user = userRepository.deserialize((ObjectNode) requireParam(call, "user"));
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonRpcMethod(accessLevel = UserRole.Admin)
|
@JsonRpcMethod(
|
||||||
|
accessLevel = UserRole.Admin,
|
||||||
|
description = "Load user object by ID",
|
||||||
|
parameters = {
|
||||||
|
@JsonRpcMethod.Parameter(name = "id", description = "User id", type = int.class)
|
||||||
|
},
|
||||||
|
returnType = ObjectNode.class
|
||||||
|
)
|
||||||
public ObjectNode getById(CallContext call) {
|
public ObjectNode getById(CallContext call) {
|
||||||
var userId = requireParam(call, "id", JsonNode::asLong);
|
var userId = requireParam(call, "id", JsonNode::asLong);
|
||||||
var userRepository = call.getContext().getService(RepositoryService.class).getRepository(UserRepository.class);
|
var userRepository = call.getContext().getService(RepositoryService.class).getRepository(UserRepository.class);
|
||||||
return userRepository.serialize(userRepository.get(userId));
|
return userRepository.serialize(userRepository.get(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonRpcMethod(
|
||||||
|
accessLevel = UserRole.Admin,
|
||||||
|
description = "Remove user by ID",
|
||||||
|
parameters = {
|
||||||
|
@JsonRpcMethod.Parameter(name = "id", description = "User id", type = int.class)
|
||||||
|
},
|
||||||
|
returnType = ObjectNode.class
|
||||||
|
)
|
||||||
|
public boolean remove(CallContext call) {
|
||||||
|
var userId = requireParam(call, "id", JsonNode::asLong);
|
||||||
|
var userRepository = call.getContext().getService(RepositoryService.class).getRepository(UserRepository.class);
|
||||||
|
var user = userRepository.get(userId);
|
||||||
|
if (user == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
userRepository.remove(user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue