diff --git a/greataped/.dockerignore b/greataped/.dockerignore new file mode 100644 index 0000000..0af94b9 --- /dev/null +++ b/greataped/.dockerignore @@ -0,0 +1,366 @@ +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,intellij+all,goland+all,go,node,macos +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,intellij+all,goland+all,go,node,macos + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +### Go Patch ### +/vendor/ +/Godeps/ + +### GoLand+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### GoLand+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# AWS User-specific + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# SonarLint plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### Intellij+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + + + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,intellij+all,goland+all,go,node,macos diff --git a/greataped/.gitignore b/greataped/.gitignore new file mode 100644 index 0000000..0af94b9 --- /dev/null +++ b/greataped/.gitignore @@ -0,0 +1,366 @@ +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,intellij+all,goland+all,go,node,macos +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,intellij+all,goland+all,go,node,macos + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +### Go Patch ### +/vendor/ +/Godeps/ + +### GoLand+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### GoLand+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# AWS User-specific + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# SonarLint plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### Intellij+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + + + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,intellij+all,goland+all,go,node,macos diff --git a/greataped/Dockerfile b/greataped/Dockerfile new file mode 100644 index 0000000..5736ede --- /dev/null +++ b/greataped/Dockerfile @@ -0,0 +1,19 @@ +FROM golang:1.19 AS builder + +WORKDIR /app + +COPY . . + +RUN CGO_ENABLED=1 GOOS=linux go build -a -ldflags '-linkmode external -extldflags "-static"' -o ./bin/greatape . + +FROM scratch + +ENV PROTOCOL="http" +ENV DOMAIN="localhost" +ENV PORT=80 + +COPY --from=builder /app/bin /app + +EXPOSE $PORT + +ENTRYPOINT ["/app/greatape"] \ No newline at end of file diff --git a/greataped/README.md b/greataped/README.md new file mode 100644 index 0000000..a8069ec --- /dev/null +++ b/greataped/README.md @@ -0,0 +1,20 @@ +[![image](https://img.shields.io/badge/Go-00A7D0?style=for-the-badge&logo=go&logoColor=white)](https://go.dev) [![image](https://img.shields.io/badge/ActivityPub-DD307D?style=for-the-badge&logoColor=white)](https://www.w3.org/TR/activitypub/) [![image](https://img.shields.io/badge/JSON--LD-FF6600?style=for-the-badge&logo=json&logoColor=white)](https://json-ld.org) [![image](https://img.shields.io/badge/MySQL-32738C?style=for-the-badge&logo=mysql&logoColor=white)](https://www.mysql.com) [![image](https://img.shields.io/badge/MariaDB-39818D?style=for-the-badge&logo=mariadb&logoColor=white)](https://mariadb.com) + +## Note + +⚠️ This project is under heavy development and should not be used in production yet. + +## APIs: +1. [Echo](#echo) + +--- + +## Echo +``` +Request: + Document document + +Result: + Document document +``` +[Back to List](#apis) diff --git a/greataped/app/commands/spi/echo.go b/greataped/app/commands/spi/echo.go new file mode 100644 index 0000000..f66a480 --- /dev/null +++ b/greataped/app/commands/spi/echo.go @@ -0,0 +1,10 @@ +package spi + +import ( + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" +) + +func Echo(x IDispatcher, document IDocument) (IEchoResult, error) { + return nil, ERROR_NOT_IMPLEMENTED +} diff --git a/greataped/app/initializer.go b/greataped/app/initializer.go new file mode 100644 index 0000000..3a9638a --- /dev/null +++ b/greataped/app/initializer.go @@ -0,0 +1,17 @@ +package functions + +import ( + "rail.town/infrastructure/app/jobs" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" +) + +func Initialize(x IDispatcher) error { + // system schedules + x.Ensure( + x.Schedule(jobs.SYSTEM_SCHEDULE_HOURLY, CRON_EVERY_HOUR, jobs.Hourly), + x.Schedule(jobs.SYSTEM_SCHEDULE_DAILY, CRON_EVERY_DAY_6PM, jobs.Daily), + ) + + return nil +} diff --git a/greataped/app/jobs/constants.go b/greataped/app/jobs/constants.go new file mode 100644 index 0000000..6527131 --- /dev/null +++ b/greataped/app/jobs/constants.go @@ -0,0 +1,7 @@ +package jobs + +const ( + // SYSTEM_SCHEDULES + SYSTEM_SCHEDULE_HOURLY = 0xD1ECB3B9 + SYSTEM_SCHEDULE_DAILY = 0xD13123C9 +) diff --git a/greataped/app/jobs/daily.go b/greataped/app/jobs/daily.go new file mode 100644 index 0000000..87b7d3f --- /dev/null +++ b/greataped/app/jobs/daily.go @@ -0,0 +1,7 @@ +package jobs + +import . "rail.town/infrastructure/components/contracts" + +func Daily(x IDispatcher, config string) { + x.Logger().Debug("✓ SYSTEM_SCHEDULE_DAILY") +} diff --git a/greataped/app/jobs/hourly.go b/greataped/app/jobs/hourly.go new file mode 100644 index 0000000..3699b59 --- /dev/null +++ b/greataped/app/jobs/hourly.go @@ -0,0 +1,7 @@ +package jobs + +import . "rail.town/infrastructure/components/contracts" + +func Hourly(x IDispatcher, config string) { + x.Logger().Debug("✓ SYSTEM_SCHEDULE_HOURLY") +} diff --git a/greataped/app/validators/initializer.go b/greataped/app/validators/initializer.go new file mode 100644 index 0000000..1672752 --- /dev/null +++ b/greataped/app/validators/initializer.go @@ -0,0 +1,5 @@ +package validators + +// noinspection GoUnusedExportedFunction +func Initialize() { +} diff --git a/greataped/app/validators/password_validator.go b/greataped/app/validators/password_validator.go new file mode 100644 index 0000000..106db79 --- /dev/null +++ b/greataped/app/validators/password_validator.go @@ -0,0 +1,34 @@ +package validators + +import ( + "unicode" +) + +func PasswordIsValid(password string) bool { + var ( + hasMinLen = false + hasUpper = false + hasLower = false + hasNumber = false + hasSpecial = false + ) + + if len(password) >= 7 { + hasMinLen = true + } + + for _, char := range password { + switch { + case unicode.IsUpper(char): + hasUpper = true + case unicode.IsLower(char): + hasLower = true + case unicode.IsNumber(char): + hasNumber = true + case unicode.IsPunct(char) || unicode.IsSymbol(char): + hasSpecial = true + } + } + + return hasMinLen && hasUpper && hasLower && hasNumber && hasSpecial +} diff --git a/greataped/app/validators/password_validator_test.go b/greataped/app/validators/password_validator_test.go new file mode 100644 index 0000000..9e0a80c --- /dev/null +++ b/greataped/app/validators/password_validator_test.go @@ -0,0 +1,49 @@ +package validators_test + +import ( + "testing" + + "rail.town/infrastructure/app/validators" +) + +func TestPasswordValidator(test *testing.T) { + type arguments struct { + password string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + "Case1", + false, + arguments{ + password: "weak_password", + }, + }, + { + "Case2", + false, + arguments{ + password: "short", + }, + }, + { + "Case3", + true, + arguments{ + password: "G0Od@PaSsw0rD", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := validators.PasswordIsValid(testCase.arguments.password); result != testCase.expectation { + test.Errorf("PasswordIsValid() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/app/validators/percent_validator.go b/greataped/app/validators/percent_validator.go new file mode 100644 index 0000000..a49eb5d --- /dev/null +++ b/greataped/app/validators/percent_validator.go @@ -0,0 +1,5 @@ +package validators + +func PercentIsValid(percent float64) bool { + return percent >= 0 && percent <= 100 +} diff --git a/greataped/app/validators/percent_validator_test.go b/greataped/app/validators/percent_validator_test.go new file mode 100644 index 0000000..958b736 --- /dev/null +++ b/greataped/app/validators/percent_validator_test.go @@ -0,0 +1,49 @@ +package validators_test + +import ( + "testing" + + "rail.town/infrastructure/app/validators" +) + +func TestPercentValidator(test *testing.T) { + type arguments struct { + percent float64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + "Case1", + true, + arguments{ + percent: 0, + }, + }, + { + "Case2", + true, + arguments{ + percent: 30, + }, + }, + { + "Case3", + false, + arguments{ + percent: 110, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := validators.PercentIsValid(testCase.arguments.percent); result != testCase.expectation { + test.Errorf("PercentIsValid() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/app/validators/postal_code_validator.go b/greataped/app/validators/postal_code_validator.go new file mode 100644 index 0000000..0306981 --- /dev/null +++ b/greataped/app/validators/postal_code_validator.go @@ -0,0 +1,17 @@ +package validators + +import "regexp" + +func PostalCodeIsValid(postalCode string) bool { + // Optional + if postalCode == "" { + return true + } + + match, err := regexp.MatchString("^\\d{10}$", postalCode) + if err != nil || !match { + return false + } + + return true +} diff --git a/greataped/app/validators/postal_code_validator_test.go b/greataped/app/validators/postal_code_validator_test.go new file mode 100644 index 0000000..8e2bcad --- /dev/null +++ b/greataped/app/validators/postal_code_validator_test.go @@ -0,0 +1,49 @@ +package validators_test + +import ( + "testing" + + "rail.town/infrastructure/app/validators" +) + +func TestPostalCodeValidator(test *testing.T) { + type arguments struct { + postalCode string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + "Case1", + true, + arguments{ + postalCode: "", + }, + }, + { + "Case2", + false, + arguments{ + postalCode: "74ah8e", + }, + }, + { + "Case3", + true, + arguments{ + postalCode: "0374829252", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := validators.PostalCodeIsValid(testCase.arguments.postalCode); result != testCase.expectation { + test.Errorf("PostalCodeIsValid() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/app/validators/required_string_validator.go b/greataped/app/validators/required_string_validator.go new file mode 100644 index 0000000..37316fe --- /dev/null +++ b/greataped/app/validators/required_string_validator.go @@ -0,0 +1,7 @@ +package validators + +import "strings" + +func RequiredStringIsValid(input string) bool { + return strings.TrimSpace(input) == "" +} diff --git a/greataped/app/validators/required_string_validator_test.go b/greataped/app/validators/required_string_validator_test.go new file mode 100644 index 0000000..a7ecb49 --- /dev/null +++ b/greataped/app/validators/required_string_validator_test.go @@ -0,0 +1,49 @@ +package validators_test + +import ( + "testing" + + "rail.town/infrastructure/app/validators" +) + +func TestRequiredStringValidator(test *testing.T) { + type arguments struct { + input string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + "Case1", + true, + arguments{ + input: "sample", + }, + }, + { + "Case2", + false, + arguments{ + input: "", + }, + }, + { + "Case3", + false, + arguments{ + input: " ", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := validators.RequiredStringIsValid(testCase.arguments.input); result != testCase.expectation { + test.Errorf("RequiredStringIsValid() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/components/api/api_test.go b/greataped/components/api/api_test.go new file mode 100644 index 0000000..d449944 --- /dev/null +++ b/greataped/components/api/api_test.go @@ -0,0 +1,95 @@ +package api_test + +import ( + "os" + "testing" + + "github.com/xeronith/diamante/analytics" + "github.com/xeronith/diamante/logging" + "github.com/xeronith/diamante/server" + "github.com/xeronith/diamante/settings" + "rail.town/infrastructure/components/api/handlers" + "rail.town/infrastructure/components/api/operations" + . "rail.town/infrastructure/components/api/protobuf" + "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + "rail.town/infrastructure/components/core" + "rail.town/infrastructure/components/model/repository" + "rail.town/infrastructure/providers/outbound/email" + "rail.town/infrastructure/providers/outbound/sms" +) + +var api IApi + +func TestReloadSystemComponentApi(test *testing.T) { + input := &SystemCallRequest{ + Command: "", + } + + if output, err := api.SystemCall(input); err != nil { + test.Fatal(err) + } else if output == nil { + test.Fail() + } +} + +func TestEchoApi(test *testing.T) { + input := &EchoRequest{ + Document: nil, + } + + if output, err := api.Echo(input); err != nil { + test.Fatal(err) + } else if output == nil { + test.Fail() + } +} + +func TestResolveErrorApi(test *testing.T) { + input := &ResolveErrorRequest{ + Document: nil, + } + + if output, err := api.ResolveError(input); err != nil { + test.Fatal(err) + } else if output == nil { + test.Fail() + } +} + +//region Initialization + +func TestMain(main *testing.M) { + logger := logging.NewLogger(false) + configuration := settings.NewTestConfiguration() + operationsFactory := operations.NewFactory() + handlersFactory := handlers.NewFactory() + measurementsProvider := analytics.NewInfluxDbProvider(configuration, logger) + emailProvider := email.NewProvider(logger) + smsProvider := sms.NewProvider(logger) + + if testServer, err := server.New(configuration, operationsFactory, handlersFactory, OPCODES); err != nil { + logger.Fatal(err) + } else { + if err := repository.Initialize(configuration, logger); err != nil { + logger.Fatal(err) + } + + if err := core.Initialize(configuration, logger); err != nil { + logger.Fatal(err) + } + + testServer.Localizer().Register(constants.Errors) + testServer.SetSecurityHandler(core.Conductor.IdentityManager()) + testServer.SetMeasurementsProvider(measurementsProvider) + testServer.SetEmailProvider(emailProvider) + testServer.SetSMSProvider(smsProvider) + + go testServer.Start() + + api = core.NewApi(testServer.PassiveEndpoint(), logger) + os.Exit(main.Run()) + } +} + +//endregion diff --git a/greataped/components/api/handlers/echo_handler.go b/greataped/components/api/handlers/echo_handler.go new file mode 100644 index 0000000..8770040 --- /dev/null +++ b/greataped/components/api/handlers/echo_handler.go @@ -0,0 +1,44 @@ +package handlers + +import ( + "net/http" + + . "github.com/xeronith/diamante/contracts/network/http" + pipeline "github.com/xeronith/diamante/network/http" + . "rail.town/infrastructure/components/api/protobuf" + . "rail.town/infrastructure/components/contracts" +) + +type echoHandler struct { +} + +func EchoHandler() IHttpHandler { + return &echoHandler{} +} + +func (handler *echoHandler) Method() string { + return http.MethodPost +} + +func (handler *echoHandler) Path() string { + return "/api/v1/echo" +} + +func (handler *echoHandler) HandlerFunc() HttpHandlerFunc { + return func(x IServerDispatcher) error { + request := &EchoRequest{} + result := &EchoResult{} + + onRequestUnmarshalled := func(request *EchoRequest) { + } + + return pipeline.Handle(x, + "echo", + ECHO_REQUEST, + ECHO_RESULT, + request, result, + onRequestUnmarshalled, + false, + ) + } +} diff --git a/greataped/components/api/handlers/factory.go b/greataped/components/api/handlers/factory.go new file mode 100644 index 0000000..f79d01d --- /dev/null +++ b/greataped/components/api/handlers/factory.go @@ -0,0 +1,15 @@ +package handlers + +import . "github.com/xeronith/diamante/contracts/network/http" + +type httpHandlerFactory struct{} + +func (factory *httpHandlerFactory) Handlers() []IHttpHandler { + return []IHttpHandler{ + EchoHandler(), // │ P . /api/v1/echo + } +} + +func NewFactory() IHttpHandlerFactory { + return &httpHandlerFactory{} +} diff --git a/greataped/components/api/operations/echo_operation.go b/greataped/components/api/operations/echo_operation.go new file mode 100644 index 0000000..c45938d --- /dev/null +++ b/greataped/components/api/operations/echo_operation.go @@ -0,0 +1,51 @@ +package operations + +import ( + . "github.com/xeronith/diamante/contracts/operation" + . "github.com/xeronith/diamante/contracts/service" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/operation" + . "rail.town/infrastructure/components/api/protobuf" + . "rail.town/infrastructure/components/api/services" + . "rail.town/infrastructure/components/contracts" +) + +type echoOperation struct { + Operation + + run func(IContext, *EchoRequest) (*EchoResult, error) +} + +func EchoOperation() IOperation { + return &echoOperation{ + run: EchoService, + } +} + +func (operation *echoOperation) Id() (ID, ID) { + return ECHO_REQUEST, ECHO_RESULT +} + +func (operation *echoOperation) InputContainer() Pointer { + return new(EchoRequest) +} + +func (operation *echoOperation) OutputContainer() Pointer { + return new(EchoResult) +} + +func (operation *echoOperation) Execute(context IContext, payload Pointer) (Pointer, error) { + return operation.run(context, payload.(*EchoRequest)) +} + +/* +func (operation *echoOperation) ExecutionTimeLimits() (Duration, Duration, Duration) { + var ( + TIME_LIMIT_WARNING Duration = 20_000_000 + TIME_LIMIT_ALERT Duration = 35_000_000 + TIME_LIMIT_CRITICAL Duration = 50_000_000 + ) + + return TIME_LIMIT_WARNING, TIME_LIMIT_ALERT, TIME_LIMIT_CRITICAL +} +*/ diff --git a/greataped/components/api/operations/factory.go b/greataped/components/api/operations/factory.go new file mode 100644 index 0000000..1c96283 --- /dev/null +++ b/greataped/components/api/operations/factory.go @@ -0,0 +1,17 @@ +package operations + +import . "github.com/xeronith/diamante/contracts/operation" + +type operationFactory struct{} + +func (factory *operationFactory) Operations() []IOperation { + return []IOperation{ + SystemCallOperation(), + EchoOperation(), + ResolveErrorOperation(), + } +} + +func NewFactory() IOperationFactory { + return &operationFactory{} +} diff --git a/greataped/components/api/operations/resolve_error_operation.go b/greataped/components/api/operations/resolve_error_operation.go new file mode 100644 index 0000000..42f5e98 --- /dev/null +++ b/greataped/components/api/operations/resolve_error_operation.go @@ -0,0 +1,51 @@ +package operations + +import ( + . "github.com/xeronith/diamante/contracts/operation" + . "github.com/xeronith/diamante/contracts/service" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/operation" + . "rail.town/infrastructure/components/api/protobuf" + . "rail.town/infrastructure/components/api/services" + . "rail.town/infrastructure/components/contracts" +) + +type resolveErrorOperation struct { + SecureOperation + + run func(IContext, *ResolveErrorRequest) (*ResolveErrorResult, error) +} + +func ResolveErrorOperation() IOperation { + return &resolveErrorOperation{ + run: ResolveErrorService, + } +} + +func (operation *resolveErrorOperation) Id() (ID, ID) { + return RESOLVE_ERROR_REQUEST, RESOLVE_ERROR_RESULT +} + +func (operation *resolveErrorOperation) InputContainer() Pointer { + return new(ResolveErrorRequest) +} + +func (operation *resolveErrorOperation) OutputContainer() Pointer { + return new(ResolveErrorResult) +} + +func (operation *resolveErrorOperation) Execute(context IContext, payload Pointer) (Pointer, error) { + return operation.run(context, payload.(*ResolveErrorRequest)) +} + +/* +func (operation *resolveErrorOperation) ExecutionTimeLimits() (Duration, Duration, Duration) { + var ( + TIME_LIMIT_WARNING Duration = 20_000_000 + TIME_LIMIT_ALERT Duration = 35_000_000 + TIME_LIMIT_CRITICAL Duration = 50_000_000 + ) + + return TIME_LIMIT_WARNING, TIME_LIMIT_ALERT, TIME_LIMIT_CRITICAL +} +*/ diff --git a/greataped/components/api/operations/system_call_operation.go b/greataped/components/api/operations/system_call_operation.go new file mode 100644 index 0000000..47ede3d --- /dev/null +++ b/greataped/components/api/operations/system_call_operation.go @@ -0,0 +1,39 @@ +package operations + +import ( + . "github.com/xeronith/diamante/contracts/operation" + . "github.com/xeronith/diamante/contracts/service" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/operation" + . "rail.town/infrastructure/components/api/protobuf" + . "rail.town/infrastructure/components/api/services" + . "rail.town/infrastructure/components/contracts" +) + +type systemCallOperation struct { + AdminOperation + + run func(IContext, *SystemCallRequest) (*SystemCallResult, error) +} + +func SystemCallOperation() IOperation { + return &systemCallOperation{ + run: SystemCallService, + } +} + +func (operation *systemCallOperation) Id() (ID, ID) { + return SYSTEM_CALL_REQUEST, SYSTEM_CALL_RESULT +} + +func (operation *systemCallOperation) InputContainer() Pointer { + return new(SystemCallRequest) +} + +func (operation *systemCallOperation) OutputContainer() Pointer { + return new(SystemCallResult) +} + +func (operation *systemCallOperation) Execute(context IContext, payload Pointer) (Pointer, error) { + return operation.run(context, payload.(*SystemCallRequest)) +} diff --git a/greataped/components/api/protobuf/core.pb.go b/greataped/components/api/protobuf/core.pb.go new file mode 100644 index 0000000..362a003 --- /dev/null +++ b/greataped/components/api/protobuf/core.pb.go @@ -0,0 +1,270 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.11.2 +// source: core.proto + +package __ + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// │ OBJECT: Error +type Error struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` +} + +func (x *Error) Reset() { + *x = Error{} + if protoimpl.UnsafeEnabled { + mi := &file_core_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Error) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Error) ProtoMessage() {} + +func (x *Error) ProtoReflect() protoreflect.Message { + mi := &file_core_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Error.ProtoReflect.Descriptor instead. +func (*Error) Descriptor() ([]byte, []int) { + return file_core_proto_rawDescGZIP(), []int{0} +} + +func (x *Error) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Error) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +// │ API: SystemCall +// │ Operation: SYSTEM_CALL_REQUEST 0x00001000 +// │ ResultType: SYSTEM_CALL_RESULT 0xF0001000 +type SystemCallRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"` +} + +func (x *SystemCallRequest) Reset() { + *x = SystemCallRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_core_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SystemCallRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SystemCallRequest) ProtoMessage() {} + +func (x *SystemCallRequest) ProtoReflect() protoreflect.Message { + mi := &file_core_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SystemCallRequest.ProtoReflect.Descriptor instead. +func (*SystemCallRequest) Descriptor() ([]byte, []int) { + return file_core_proto_rawDescGZIP(), []int{1} +} + +func (x *SystemCallRequest) GetCommand() string { + if x != nil { + return x.Command + } + return "" +} + +type SystemCallResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SystemCallResult) Reset() { + *x = SystemCallResult{} + if protoimpl.UnsafeEnabled { + mi := &file_core_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SystemCallResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SystemCallResult) ProtoMessage() {} + +func (x *SystemCallResult) ProtoReflect() protoreflect.Message { + mi := &file_core_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SystemCallResult.ProtoReflect.Descriptor instead. +func (*SystemCallResult) Descriptor() ([]byte, []int) { + return file_core_proto_rawDescGZIP(), []int{2} +} + +var File_core_proto protoreflect.FileDescriptor + +var file_core_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, 0x43, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2d, 0x0a, 0x11, 0x53, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x42, 0x04, + 0x5a, 0x02, 0x2e, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_core_proto_rawDescOnce sync.Once + file_core_proto_rawDescData = file_core_proto_rawDesc +) + +func file_core_proto_rawDescGZIP() []byte { + file_core_proto_rawDescOnce.Do(func() { + file_core_proto_rawDescData = protoimpl.X.CompressGZIP(file_core_proto_rawDescData) + }) + return file_core_proto_rawDescData +} + +var file_core_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_core_proto_goTypes = []interface{}{ + (*Error)(nil), // 0: protobuf.Error + (*SystemCallRequest)(nil), // 1: protobuf.SystemCallRequest + (*SystemCallResult)(nil), // 2: protobuf.SystemCallResult +} +var file_core_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_core_proto_init() } +func file_core_proto_init() { + if File_core_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_core_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Error); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SystemCallRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SystemCallResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_core_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_core_proto_goTypes, + DependencyIndexes: file_core_proto_depIdxs, + MessageInfos: file_core_proto_msgTypes, + }.Build() + File_core_proto = out.File + file_core_proto_rawDesc = nil + file_core_proto_goTypes = nil + file_core_proto_depIdxs = nil +} diff --git a/greataped/components/api/protobuf/custom_errors.pb.go b/greataped/components/api/protobuf/custom_errors.pb.go new file mode 100644 index 0000000..c8e4ca3 --- /dev/null +++ b/greataped/components/api/protobuf/custom_errors.pb.go @@ -0,0 +1,204 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.11.2 +// source: custom_errors.proto + +package __ + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// API: ResolveError +// Operation: RESOLVE_ERROR_REQUEST 0x18B1805B +// ResultType: RESOLVE_ERROR_RESULT 0xEC02BAC4 +// ------------------------------------------------------------------------------ +type ResolveErrorRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Document *Document `protobuf:"bytes,1,opt,name=document,proto3" json:"document,omitempty"` +} + +func (x *ResolveErrorRequest) Reset() { + *x = ResolveErrorRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_custom_errors_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResolveErrorRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResolveErrorRequest) ProtoMessage() {} + +func (x *ResolveErrorRequest) ProtoReflect() protoreflect.Message { + mi := &file_custom_errors_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResolveErrorRequest.ProtoReflect.Descriptor instead. +func (*ResolveErrorRequest) Descriptor() ([]byte, []int) { + return file_custom_errors_proto_rawDescGZIP(), []int{0} +} + +func (x *ResolveErrorRequest) GetDocument() *Document { + if x != nil { + return x.Document + } + return nil +} + +type ResolveErrorResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ResolveErrorResult) Reset() { + *x = ResolveErrorResult{} + if protoimpl.UnsafeEnabled { + mi := &file_custom_errors_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResolveErrorResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResolveErrorResult) ProtoMessage() {} + +func (x *ResolveErrorResult) ProtoReflect() protoreflect.Message { + mi := &file_custom_errors_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResolveErrorResult.ProtoReflect.Descriptor instead. +func (*ResolveErrorResult) Descriptor() ([]byte, []int) { + return file_custom_errors_proto_rawDescGZIP(), []int{1} +} + +var File_custom_errors_proto protoreflect.FileDescriptor + +var file_custom_errors_proto_rawDesc = []byte{ + 0x0a, 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x1a, + 0x0d, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x45, + 0x0a, 0x13, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x64, 0x6f, 0x63, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x14, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x42, 0x04, 0x5a, 0x02, 0x2e, + 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_custom_errors_proto_rawDescOnce sync.Once + file_custom_errors_proto_rawDescData = file_custom_errors_proto_rawDesc +) + +func file_custom_errors_proto_rawDescGZIP() []byte { + file_custom_errors_proto_rawDescOnce.Do(func() { + file_custom_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_custom_errors_proto_rawDescData) + }) + return file_custom_errors_proto_rawDescData +} + +var file_custom_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_custom_errors_proto_goTypes = []interface{}{ + (*ResolveErrorRequest)(nil), // 0: protobuf.ResolveErrorRequest + (*ResolveErrorResult)(nil), // 1: protobuf.ResolveErrorResult + (*Document)(nil), // 2: protobuf.Document +} +var file_custom_errors_proto_depIdxs = []int32{ + 2, // 0: protobuf.ResolveErrorRequest.document:type_name -> protobuf.Document + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_custom_errors_proto_init() } +func file_custom_errors_proto_init() { + if File_custom_errors_proto != nil { + return + } + file_objects_proto_init() + if !protoimpl.UnsafeEnabled { + file_custom_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResolveErrorRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_custom_errors_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResolveErrorResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_custom_errors_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_custom_errors_proto_goTypes, + DependencyIndexes: file_custom_errors_proto_depIdxs, + MessageInfos: file_custom_errors_proto_msgTypes, + }.Build() + File_custom_errors_proto = out.File + file_custom_errors_proto_rawDesc = nil + file_custom_errors_proto_goTypes = nil + file_custom_errors_proto_depIdxs = nil +} diff --git a/greataped/components/api/protobuf/messages/core.proto b/greataped/components/api/protobuf/messages/core.proto new file mode 100644 index 0000000..36fcb32 --- /dev/null +++ b/greataped/components/api/protobuf/messages/core.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package protobuf; + +option go_package = "./"; + +// │ OBJECT: Error +message Error { + string message = 1; + string description = 2; +} + +// │ API: SystemCall +// │ Operation: SYSTEM_CALL_REQUEST 0x00001000 +// │ ResultType: SYSTEM_CALL_RESULT 0xF0001000 +message SystemCallRequest { + string command = 1; +} + +message SystemCallResult { +} diff --git a/greataped/components/api/protobuf/messages/custom_errors.proto b/greataped/components/api/protobuf/messages/custom_errors.proto new file mode 100644 index 0000000..ad7d95f --- /dev/null +++ b/greataped/components/api/protobuf/messages/custom_errors.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package protobuf; + +option go_package = "./"; + +import "objects.proto"; + +// API: ResolveError +// Operation: RESOLVE_ERROR_REQUEST 0x18B1805B +// ResultType: RESOLVE_ERROR_RESULT 0xEC02BAC4 +//------------------------------------------------------------------------------ +message ResolveErrorRequest { + Document document = 0x00000001; +} + +message ResolveErrorResult { +} + +//------------------------------------------------------------------------------ diff --git a/greataped/components/api/protobuf/messages/objects.proto b/greataped/components/api/protobuf/messages/objects.proto new file mode 100644 index 0000000..613b6a7 --- /dev/null +++ b/greataped/components/api/protobuf/messages/objects.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package protobuf; + +option go_package = "./"; + +// | OBJECT: Document +message Document { + int64 id = 0x00000001; + string content = 0x00000002; +} + +// | OBJECT: User +message User { + int64 id = 0x00000001; + string github = 0x00000003; +} diff --git a/greataped/components/api/protobuf/messages/spis.proto b/greataped/components/api/protobuf/messages/spis.proto new file mode 100644 index 0000000..b3d4daa --- /dev/null +++ b/greataped/components/api/protobuf/messages/spis.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package protobuf; + +option go_package = "./"; + +import "objects.proto"; + +// API: Echo +// Operation: ECHO_REQUEST 0x0541BD72 +// ResultType: ECHO_RESULT 0xAB2FF7D4 +//------------------------------------------------------------------------------ +message EchoRequest { + Document document = 0x00000001; +} + +message EchoResult { + Document document = 0x00000001; +} + +//------------------------------------------------------------------------------ diff --git a/greataped/components/api/protobuf/objects.pb.go b/greataped/components/api/protobuf/objects.pb.go new file mode 100644 index 0000000..5179983 --- /dev/null +++ b/greataped/components/api/protobuf/objects.pb.go @@ -0,0 +1,223 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.11.2 +// source: objects.proto + +package __ + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// | OBJECT: Document +type Document struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Content string `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` +} + +func (x *Document) Reset() { + *x = Document{} + if protoimpl.UnsafeEnabled { + mi := &file_objects_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Document) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Document) ProtoMessage() {} + +func (x *Document) ProtoReflect() protoreflect.Message { + mi := &file_objects_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Document.ProtoReflect.Descriptor instead. +func (*Document) Descriptor() ([]byte, []int) { + return file_objects_proto_rawDescGZIP(), []int{0} +} + +func (x *Document) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Document) GetContent() string { + if x != nil { + return x.Content + } + return "" +} + +// | OBJECT: User +type User struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Github string `protobuf:"bytes,3,opt,name=github,proto3" json:"github,omitempty"` +} + +func (x *User) Reset() { + *x = User{} + if protoimpl.UnsafeEnabled { + mi := &file_objects_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_objects_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_objects_proto_rawDescGZIP(), []int{1} +} + +func (x *User) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *User) GetGithub() string { + if x != nil { + return x.Github + } + return "" +} + +var File_objects_proto protoreflect.FileDescriptor + +var file_objects_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, 0x34, 0x0a, 0x08, 0x44, 0x6f, 0x63, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, + 0x2e, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x42, + 0x04, 0x5a, 0x02, 0x2e, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_objects_proto_rawDescOnce sync.Once + file_objects_proto_rawDescData = file_objects_proto_rawDesc +) + +func file_objects_proto_rawDescGZIP() []byte { + file_objects_proto_rawDescOnce.Do(func() { + file_objects_proto_rawDescData = protoimpl.X.CompressGZIP(file_objects_proto_rawDescData) + }) + return file_objects_proto_rawDescData +} + +var file_objects_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_objects_proto_goTypes = []interface{}{ + (*Document)(nil), // 0: protobuf.Document + (*User)(nil), // 1: protobuf.User +} +var file_objects_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_objects_proto_init() } +func file_objects_proto_init() { + if File_objects_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_objects_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Document); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_objects_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*User); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_objects_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_objects_proto_goTypes, + DependencyIndexes: file_objects_proto_depIdxs, + MessageInfos: file_objects_proto_msgTypes, + }.Build() + File_objects_proto = out.File + file_objects_proto_rawDesc = nil + file_objects_proto_goTypes = nil + file_objects_proto_depIdxs = nil +} diff --git a/greataped/components/api/protobuf/spis.pb.go b/greataped/components/api/protobuf/spis.pb.go new file mode 100644 index 0000000..66af384 --- /dev/null +++ b/greataped/components/api/protobuf/spis.pb.go @@ -0,0 +1,215 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.11.2 +// source: spis.proto + +package __ + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// API: Echo +// Operation: ECHO_REQUEST 0x0541BD72 +// ResultType: ECHO_RESULT 0xAB2FF7D4 +// ------------------------------------------------------------------------------ +type EchoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Document *Document `protobuf:"bytes,1,opt,name=document,proto3" json:"document,omitempty"` +} + +func (x *EchoRequest) Reset() { + *x = EchoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_spis_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EchoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EchoRequest) ProtoMessage() {} + +func (x *EchoRequest) ProtoReflect() protoreflect.Message { + mi := &file_spis_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EchoRequest.ProtoReflect.Descriptor instead. +func (*EchoRequest) Descriptor() ([]byte, []int) { + return file_spis_proto_rawDescGZIP(), []int{0} +} + +func (x *EchoRequest) GetDocument() *Document { + if x != nil { + return x.Document + } + return nil +} + +type EchoResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Document *Document `protobuf:"bytes,1,opt,name=document,proto3" json:"document,omitempty"` +} + +func (x *EchoResult) Reset() { + *x = EchoResult{} + if protoimpl.UnsafeEnabled { + mi := &file_spis_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EchoResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EchoResult) ProtoMessage() {} + +func (x *EchoResult) ProtoReflect() protoreflect.Message { + mi := &file_spis_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EchoResult.ProtoReflect.Descriptor instead. +func (*EchoResult) Descriptor() ([]byte, []int) { + return file_spis_proto_rawDescGZIP(), []int{1} +} + +func (x *EchoResult) GetDocument() *Document { + if x != nil { + return x.Document + } + return nil +} + +var File_spis_proto protoreflect.FileDescriptor + +var file_spis_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x73, 0x70, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x1a, 0x0d, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3d, 0x0a, 0x0b, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x64, 0x6f, 0x63, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x3c, 0x0a, 0x0a, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x42, 0x04, 0x5a, 0x02, 0x2e, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_spis_proto_rawDescOnce sync.Once + file_spis_proto_rawDescData = file_spis_proto_rawDesc +) + +func file_spis_proto_rawDescGZIP() []byte { + file_spis_proto_rawDescOnce.Do(func() { + file_spis_proto_rawDescData = protoimpl.X.CompressGZIP(file_spis_proto_rawDescData) + }) + return file_spis_proto_rawDescData +} + +var file_spis_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_spis_proto_goTypes = []interface{}{ + (*EchoRequest)(nil), // 0: protobuf.EchoRequest + (*EchoResult)(nil), // 1: protobuf.EchoResult + (*Document)(nil), // 2: protobuf.Document +} +var file_spis_proto_depIdxs = []int32{ + 2, // 0: protobuf.EchoRequest.document:type_name -> protobuf.Document + 2, // 1: protobuf.EchoResult.document:type_name -> protobuf.Document + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_spis_proto_init() } +func file_spis_proto_init() { + if File_spis_proto != nil { + return + } + file_objects_proto_init() + if !protoimpl.UnsafeEnabled { + file_spis_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EchoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_spis_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EchoResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_spis_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_spis_proto_goTypes, + DependencyIndexes: file_spis_proto_depIdxs, + MessageInfos: file_spis_proto_msgTypes, + }.Build() + File_spis_proto = out.File + file_spis_proto_rawDesc = nil + file_spis_proto_goTypes = nil + file_spis_proto_depIdxs = nil +} diff --git a/greataped/components/api/services/echo_service.go b/greataped/components/api/services/echo_service.go new file mode 100644 index 0000000..3f26f4d --- /dev/null +++ b/greataped/components/api/services/echo_service.go @@ -0,0 +1,46 @@ +package services + +import ( + . "github.com/xeronith/diamante/contracts/service" + . "rail.town/infrastructure/components/api/protobuf" + . "rail.town/infrastructure/components/contracts" + "rail.town/infrastructure/components/core" +) + +// noinspection GoUnusedParameter +func EchoService(context IContext, input *EchoRequest) (result *EchoResult, err error) { + conductor := core.Conductor + _ = ECHO_REQUEST + + conductor.LogRemoteCall(context, INITIALIZE, "echo", input, result, err) + defer func() { conductor.LogRemoteCall(context, FINALIZE, "echo", input, result, err) }() + + var inputDocument IDocument + if input.Document != nil { + var err error + if inputDocument, err = conductor.NewDocument(input.Document.Id, input.Document.Content); err == nil { + } else { + return nil, err + } + } + + _result, _err := conductor.Echo(inputDocument, context.Identity()) + if _err != nil { + err = _err + return nil, err + } + + _ = _result + + var outputDocument *Document = nil + if _result.Document() != nil { + outputDocument = &Document{ + Id: _result.Document().Id(), + Content: _result.Document().Content(), + } + } + + result = context.ResultContainer().(*EchoResult) + result.Document = outputDocument + return result, nil +} diff --git a/greataped/components/api/services/resolve_error_service.go b/greataped/components/api/services/resolve_error_service.go new file mode 100644 index 0000000..107eb3f --- /dev/null +++ b/greataped/components/api/services/resolve_error_service.go @@ -0,0 +1,37 @@ +package services + +import ( + . "github.com/xeronith/diamante/contracts/service" + . "rail.town/infrastructure/components/api/protobuf" + . "rail.town/infrastructure/components/contracts" + "rail.town/infrastructure/components/core" +) + +// noinspection GoUnusedParameter +func ResolveErrorService(context IContext, input *ResolveErrorRequest) (result *ResolveErrorResult, err error) { + conductor := core.Conductor + _ = RESOLVE_ERROR_REQUEST + + conductor.LogRemoteCall(context, INITIALIZE, "resolve_error", input, result, err) + defer func() { conductor.LogRemoteCall(context, FINALIZE, "resolve_error", input, result, err) }() + + var inputDocument IDocument + if input.Document != nil { + var err error + if inputDocument, err = conductor.NewDocument(input.Document.Id, input.Document.Content); err == nil { + } else { + return nil, err + } + } + + _result, _err := conductor.ResolveError(inputDocument, context.Identity()) + if _err != nil { + err = _err + return nil, err + } + + _ = _result + + result = context.ResultContainer().(*ResolveErrorResult) + return result, nil +} diff --git a/greataped/components/api/services/system_call_service.go b/greataped/components/api/services/system_call_service.go new file mode 100644 index 0000000..97db1b0 --- /dev/null +++ b/greataped/components/api/services/system_call_service.go @@ -0,0 +1,46 @@ +package services + +import ( + "fmt" + "strings" + + . "github.com/xeronith/diamante/contracts/service" + . "rail.town/infrastructure/components/api/protobuf" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + "rail.town/infrastructure/components/core" +) + +func SystemCallService(context IContext, input *SystemCallRequest) (result *SystemCallResult, err error) { + core.Conductor.LogRemoteCall(context, INITIALIZE, "system_call", input, result, err) + defer func() { core.Conductor.LogRemoteCall(context, FINALIZE, "system_call", input, result, err) }() + + context.Logger().SysCall(fmt.Sprintf("SYSCALL: %s", input.Command)) + + args := strings.Split(input.Command, " ") + if len(args) < 1 { + return nil, ERROR_NOT_IMPLEMENTED + } + + switch args[0] { + case "reload": + if len(args) < 2 { + return nil, ERROR_NOT_IMPLEMENTED + } + + componentName := args[1] + if component := core.Conductor.GetSystemComponent(componentName); component == nil { + return nil, ERROR_SYSTEM_COMPONENT_NOT_FOUND + } else if err := component.Reload(); err != nil { + return nil, err + } else { + return context.ResultContainer().(*SystemCallResult), nil + } + default: + if err := context.SystemCall(args); err != nil { + return nil, err + } + + return context.ResultContainer().(*SystemCallResult), nil + } +} diff --git a/greataped/components/config.yaml b/greataped/components/config.yaml new file mode 100644 index 0000000..b5111c5 --- /dev/null +++ b/greataped/components/config.yaml @@ -0,0 +1,9 @@ +environment: development +server: + protocol: http + fqdn: localhost + hash_key: 'OKq2gLmDCYJXnPweKrM=l7dFCDxp5Ff5EupcQCU' + block_key: 'v1K1s+S3vWudrypR' +mysql: + username: root + password: password diff --git a/greataped/components/constants/defaults.go b/greataped/components/constants/defaults.go new file mode 100644 index 0000000..327926c --- /dev/null +++ b/greataped/components/constants/defaults.go @@ -0,0 +1,23 @@ +package constants + +// noinspection GoSnakeCaseUsage, GoUnusedConst +const ( + EMPTY = "" + EMPTY_JSON = "{}" + NOT_SET = 0 + + // ACL_ROLE + ACL_PERMISSION_USER = 1 + ACL_PERMISSION_ADMIN = 4294967295 + + // ACL_RESTRICTION + ACL_RESTRICTION_NONE = 0 + ACL_RESTRICTION_ACCESS_DENIED = 1 + + // CRON + CRON_EVERY_10_SECONDS = "0/10 * * * * ?" + CRON_EVERY_MINUTE = "0 * * * * ?" + CRON_EVERY_TEN_MINUTES = "0 0/10 * * * ?" + CRON_EVERY_DAY_6PM = "0 0 18 * * ?" + CRON_EVERY_HOUR = "0 0 * * * ?" +) diff --git a/greataped/components/constants/errors.go b/greataped/components/constants/errors.go new file mode 100644 index 0000000..7ea369d --- /dev/null +++ b/greataped/components/constants/errors.go @@ -0,0 +1,96 @@ +package constants + +import "errors" + +// noinspection GoSnakeCaseUsage +const ENABLE_CUSTOM_ERRORS = true + +// noinspection GoSnakeCaseUsage +const ( + // SYSTEM_ERRORS + ERROR_MESSAGE_INITIALIZE = "ERROR_MESSAGE_INITIALIZE" + ERROR_MESSAGE_NOT_IMPLEMENTED = "ERROR_MESSAGE_NOT_IMPLEMENTED" + ERROR_MESSAGE_OPERATION_FAILED = "ERROR_MESSAGE_OPERATION_FAILED" + ERROR_MESSAGE_OPERATION_NOT_SUPPORTED = "ERROR_MESSAGE_OPERATION_NOT_SUPPORTED" + ERROR_MESSAGE_UNRESOLVED_DEPENDENCIES = "ERROR_MESSAGE_UNRESOLVED_DEPENDENCIES" + ERROR_MESSAGE_SYSTEM_COMPONENT_NOT_FOUND = "ERROR_MESSAGE_SYSTEM_COMPONENT_NOT_FOUND" + ERROR_MESSAGE_DOCUMENT_NOT_FOUND = "ERROR_MESSAGE_DOCUMENT_NOT_FOUND" + ERROR_MESSAGE_SYSTEM_SCHEDULE_NOT_FOUND = "ERROR_MESSAGE_SYSTEM_SCHEDULE_NOT_FOUND" + ERROR_MESSAGE_IDENTITY_NOT_FOUND = "ERROR_MESSAGE_IDENTITY_NOT_FOUND" + ERROR_MESSAGE_ACCESS_CONTROL_NOT_FOUND = "ERROR_MESSAGE_ACCESS_CONTROL_NOT_FOUND" + ERROR_MESSAGE_REMOTE_ACTIVITY_NOT_FOUND = "ERROR_MESSAGE_REMOTE_ACTIVITY_NOT_FOUND" + ERROR_MESSAGE_CATEGORY_TYPE_NOT_FOUND = "ERROR_MESSAGE_CATEGORY_TYPE_NOT_FOUND" + ERROR_MESSAGE_CATEGORY_NOT_FOUND = "ERROR_MESSAGE_CATEGORY_NOT_FOUND" + ERROR_MESSAGE_USER_NOT_FOUND = "ERROR_MESSAGE_USER_NOT_FOUND" + ERROR_MESSAGE_SPI_NOT_FOUND = "ERROR_MESSAGE_SPI_NOT_FOUND" + ERROR_MESSAGE_CUSTOM_ERROR_NOT_FOUND = "ERROR_MESSAGE_CUSTOM_ERROR_NOT_FOUND" + ERROR_MESSAGE_UNKNOWN_DOCUMENT = "ERROR_MESSAGE_UNKNOWN_DOCUMENT" + ERROR_MESSAGE_UNKNOWN_SYSTEM_SCHEDULE = "ERROR_MESSAGE_UNKNOWN_SYSTEM_SCHEDULE" + ERROR_MESSAGE_UNKNOWN_IDENTITY = "ERROR_MESSAGE_UNKNOWN_IDENTITY" + ERROR_MESSAGE_UNKNOWN_ACCESS_CONTROL = "ERROR_MESSAGE_UNKNOWN_ACCESS_CONTROL" + ERROR_MESSAGE_UNKNOWN_REMOTE_ACTIVITY = "ERROR_MESSAGE_UNKNOWN_REMOTE_ACTIVITY" + ERROR_MESSAGE_UNKNOWN_CATEGORY_TYPE = "ERROR_MESSAGE_UNKNOWN_CATEGORY_TYPE" + ERROR_MESSAGE_UNKNOWN_CATEGORY = "ERROR_MESSAGE_UNKNOWN_CATEGORY" + ERROR_MESSAGE_UNKNOWN_USER = "ERROR_MESSAGE_UNKNOWN_USER" + ERROR_MESSAGE_UNKNOWN_SPI = "ERROR_MESSAGE_UNKNOWN_SPI" + ERROR_MESSAGE_UNKNOWN_CUSTOM_ERROR = "ERROR_MESSAGE_UNKNOWN_CUSTOM_ERROR" + ERROR_MESSAGE_INVALID_ID = "ERROR_MESSAGE_INVALID_ID" + ERROR_MESSAGE_INVALID_PARAMETERS = "ERROR_MESSAGE_INVALID_PARAMETERS" + // CUSTOM_ERRORS + ERROR_MESSAGE_DATA_INTEGRITY_VIOLATION = "ERROR_MESSAGE_DATA_INTEGRITY_VIOLATION" + ERROR_MESSAGE_INVALID_STATE = "ERROR_MESSAGE_INVALID_STATE" + ERROR_MESSAGE_USER_NOT_REGISTERED = "ERROR_MESSAGE_USER_NOT_REGISTERED" + ERROR_MESSAGE_USERNAME_OR_EMAIL_ALREADY_REGISTERED = "ERROR_MESSAGE_USERNAME_OR_EMAIL_ALREADY_REGISTERED" + ERROR_MESSAGE_ACCOUNT_NOT_VERIFIED = "ERROR_MESSAGE_ACCOUNT_NOT_VERIFIED" + ERROR_MESSAGE_ACCOUNT_BLOCKED = "ERROR_MESSAGE_ACCOUNT_BLOCKED" + ERROR_MESSAGE_INVALID_TOKEN = "ERROR_MESSAGE_INVALID_TOKEN" + ERROR_MESSAGE_INVALID_CONFIRMATION_CODE = "ERROR_MESSAGE_INVALID_CONFIRMATION_CODE" + ERROR_MESSAGE_PERMISSION_DENIED = "ERROR_MESSAGE_PERMISSION_DENIED" + ERROR_MESSAGE_INVALID_PERSON_KIND = "ERROR_MESSAGE_INVALID_PERSON_KIND" + ERROR_MESSAGE_INVALID_CREDENTIALS = "ERROR_MESSAGE_INVALID_CREDENTIALS" +) + +// noinspection GoSnakeCaseUsage,GoUnusedGlobalVariable +var ( + // SYSTEM_ERRORS + ERROR_INITIALIZE = errors.New(ERROR_MESSAGE_INITIALIZE) + ERROR_NOT_IMPLEMENTED = errors.New(ERROR_MESSAGE_NOT_IMPLEMENTED) + ERROR_OPERATION_FAILED = errors.New(ERROR_MESSAGE_OPERATION_FAILED) + ERROR_OPERATION_NOT_SUPPORTED = errors.New(ERROR_MESSAGE_OPERATION_NOT_SUPPORTED) + ERROR_UNRESOLVED_DEPENDENCIES = errors.New(ERROR_MESSAGE_UNRESOLVED_DEPENDENCIES) + ERROR_SYSTEM_COMPONENT_NOT_FOUND = errors.New(ERROR_MESSAGE_SYSTEM_COMPONENT_NOT_FOUND) + ERROR_DOCUMENT_NOT_FOUND = errors.New(ERROR_MESSAGE_DOCUMENT_NOT_FOUND) + ERROR_SYSTEM_SCHEDULE_NOT_FOUND = errors.New(ERROR_MESSAGE_SYSTEM_SCHEDULE_NOT_FOUND) + ERROR_IDENTITY_NOT_FOUND = errors.New(ERROR_MESSAGE_IDENTITY_NOT_FOUND) + ERROR_ACCESS_CONTROL_NOT_FOUND = errors.New(ERROR_MESSAGE_ACCESS_CONTROL_NOT_FOUND) + ERROR_REMOTE_ACTIVITY_NOT_FOUND = errors.New(ERROR_MESSAGE_REMOTE_ACTIVITY_NOT_FOUND) + ERROR_CATEGORY_TYPE_NOT_FOUND = errors.New(ERROR_MESSAGE_CATEGORY_TYPE_NOT_FOUND) + ERROR_CATEGORY_NOT_FOUND = errors.New(ERROR_MESSAGE_CATEGORY_NOT_FOUND) + ERROR_USER_NOT_FOUND = errors.New(ERROR_MESSAGE_USER_NOT_FOUND) + ERROR_SPI_NOT_FOUND = errors.New(ERROR_MESSAGE_SPI_NOT_FOUND) + ERROR_CUSTOM_ERROR_NOT_FOUND = errors.New(ERROR_MESSAGE_CUSTOM_ERROR_NOT_FOUND) + ERROR_UNKNOWN_DOCUMENT = errors.New(ERROR_MESSAGE_UNKNOWN_DOCUMENT) + ERROR_UNKNOWN_SYSTEM_SCHEDULE = errors.New(ERROR_MESSAGE_UNKNOWN_SYSTEM_SCHEDULE) + ERROR_UNKNOWN_IDENTITY = errors.New(ERROR_MESSAGE_UNKNOWN_IDENTITY) + ERROR_UNKNOWN_ACCESS_CONTROL = errors.New(ERROR_MESSAGE_UNKNOWN_ACCESS_CONTROL) + ERROR_UNKNOWN_REMOTE_ACTIVITY = errors.New(ERROR_MESSAGE_UNKNOWN_REMOTE_ACTIVITY) + ERROR_UNKNOWN_CATEGORY_TYPE = errors.New(ERROR_MESSAGE_UNKNOWN_CATEGORY_TYPE) + ERROR_UNKNOWN_CATEGORY = errors.New(ERROR_MESSAGE_UNKNOWN_CATEGORY) + ERROR_UNKNOWN_USER = errors.New(ERROR_MESSAGE_UNKNOWN_USER) + ERROR_UNKNOWN_SPI = errors.New(ERROR_MESSAGE_UNKNOWN_SPI) + ERROR_UNKNOWN_CUSTOM_ERROR = errors.New(ERROR_MESSAGE_UNKNOWN_CUSTOM_ERROR) + ERROR_INVALID_ID = errors.New(ERROR_MESSAGE_INVALID_ID) + ERROR_INVALID_PARAMETERS = errors.New(ERROR_MESSAGE_INVALID_PARAMETERS) + // CUSTOM_ERRORS + ERROR_DATA_INTEGRITY_VIOLATION = errors.New(ERROR_MESSAGE_DATA_INTEGRITY_VIOLATION) + ERROR_INVALID_STATE = errors.New(ERROR_MESSAGE_INVALID_STATE) + ERROR_USER_NOT_REGISTERED = errors.New(ERROR_MESSAGE_USER_NOT_REGISTERED) + ERROR_USERNAME_OR_EMAIL_ALREADY_REGISTERED = errors.New(ERROR_MESSAGE_USERNAME_OR_EMAIL_ALREADY_REGISTERED) + ERROR_ACCOUNT_NOT_VERIFIED = errors.New(ERROR_MESSAGE_ACCOUNT_NOT_VERIFIED) + ERROR_ACCOUNT_BLOCKED = errors.New(ERROR_MESSAGE_ACCOUNT_BLOCKED) + ERROR_INVALID_TOKEN = errors.New(ERROR_MESSAGE_INVALID_TOKEN) + ERROR_INVALID_CONFIRMATION_CODE = errors.New(ERROR_MESSAGE_INVALID_CONFIRMATION_CODE) + ERROR_PERMISSION_DENIED = errors.New(ERROR_MESSAGE_PERMISSION_DENIED) + ERROR_INVALID_PERSON_KIND = errors.New(ERROR_MESSAGE_INVALID_PERSON_KIND) + ERROR_INVALID_CREDENTIALS = errors.New(ERROR_MESSAGE_INVALID_CREDENTIALS) +) diff --git a/greataped/components/constants/regex.go b/greataped/components/constants/regex.go new file mode 100644 index 0000000..43b8ffa --- /dev/null +++ b/greataped/components/constants/regex.go @@ -0,0 +1,11 @@ +package constants + +// noinspection GoSnakeCaseUsage, GoUnusedConst +const ( + USERNAME = "^[a-z0-9_\\.]{5,16}$" + PASSWORD = "^.{6,}$" + PHONE_NUMBER = "^9\\d{9}$" + URL = "^(?:(?:https?|ftp):\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))\\.?)(?::\\d{2,5})?(?:[/?#]\\S*)?$" + EMAIL = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$" + WEBFINGER = "^acct:[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$" +) diff --git a/greataped/components/constants/resources.en-US.go b/greataped/components/constants/resources.en-US.go new file mode 100644 index 0000000..2430984 --- /dev/null +++ b/greataped/components/constants/resources.en-US.go @@ -0,0 +1,53 @@ +package constants + +import . "github.com/xeronith/diamante/contracts/localization" + +// noinspection GoUnusedGlobalVariable +var Errors = Resource{ + // SYSTEM_ERRORS + ERROR_MESSAGE_INITIALIZE: "initialize", + ERROR_MESSAGE_NOT_IMPLEMENTED: "not_implemented", + ERROR_MESSAGE_OPERATION_FAILED: "operation_failed", + ERROR_MESSAGE_OPERATION_NOT_SUPPORTED: "operation_not_supported", + ERROR_MESSAGE_UNRESOLVED_DEPENDENCIES: "unresolved_dependencies", + ERROR_MESSAGE_SYSTEM_COMPONENT_NOT_FOUND: "system_component_not_found", + ERROR_MESSAGE_DOCUMENT_NOT_FOUND: "document_not_found", + ERROR_MESSAGE_SYSTEM_SCHEDULE_NOT_FOUND: "system_schedule_not_found", + ERROR_MESSAGE_IDENTITY_NOT_FOUND: "identity_not_found", + ERROR_MESSAGE_ACCESS_CONTROL_NOT_FOUND: "access_control_not_found", + ERROR_MESSAGE_REMOTE_ACTIVITY_NOT_FOUND: "remote_activity_not_found", + ERROR_MESSAGE_CATEGORY_TYPE_NOT_FOUND: "category_type_not_found", + ERROR_MESSAGE_CATEGORY_NOT_FOUND: "category_not_found", + ERROR_MESSAGE_USER_NOT_FOUND: "user_not_found", + ERROR_MESSAGE_SPI_NOT_FOUND: "spi_not_found", + ERROR_MESSAGE_CUSTOM_ERROR_NOT_FOUND: "custom_error_not_found", + ERROR_MESSAGE_UNKNOWN_DOCUMENT: "unknown_document", + ERROR_MESSAGE_UNKNOWN_SYSTEM_SCHEDULE: "unknown_system_schedule", + ERROR_MESSAGE_UNKNOWN_IDENTITY: "unknown_identity", + ERROR_MESSAGE_UNKNOWN_ACCESS_CONTROL: "unknown_access_control", + ERROR_MESSAGE_UNKNOWN_REMOTE_ACTIVITY: "unknown_remote_activity", + ERROR_MESSAGE_UNKNOWN_CATEGORY_TYPE: "unknown_category_type", + ERROR_MESSAGE_UNKNOWN_CATEGORY: "unknown_category", + ERROR_MESSAGE_UNKNOWN_USER: "unknown_user", + ERROR_MESSAGE_UNKNOWN_SPI: "unknown_spi", + ERROR_MESSAGE_UNKNOWN_CUSTOM_ERROR: "unknown_custom_error", + ERROR_MESSAGE_INVALID_ID: "invalid_id", + ERROR_MESSAGE_INVALID_PARAMETERS: "invalid_parameters", + // CUSTOM_ERRORS + ERROR_MESSAGE_DATA_INTEGRITY_VIOLATION: "data_integrity_violation", + ERROR_MESSAGE_INVALID_STATE: "invalid_state", + ERROR_MESSAGE_USER_NOT_REGISTERED: "user_not_registered", + ERROR_MESSAGE_USERNAME_OR_EMAIL_ALREADY_REGISTERED: "username_or_email_already_registered", + ERROR_MESSAGE_ACCOUNT_NOT_VERIFIED: "account_not_verified", + ERROR_MESSAGE_ACCOUNT_BLOCKED: "account_blocked", + ERROR_MESSAGE_INVALID_TOKEN: "invalid_token", + ERROR_MESSAGE_INVALID_CONFIRMATION_CODE: "invalid_confirmation_code", + ERROR_MESSAGE_PERMISSION_DENIED: "permission_denied", + ERROR_MESSAGE_INVALID_PERSON_KIND: "invalid_person_kind", + ERROR_MESSAGE_INVALID_CREDENTIALS: "invalid_credentials", +} + +func init() { + // CUSTOM_ERRORS + Errors[ERROR_MESSAGE_DATA_INTEGRITY_VIOLATION] = "data_integrity_violation" +} diff --git a/greataped/components/contracts/access_control.go b/greataped/components/contracts/access_control.go new file mode 100644 index 0000000..5d28b1e --- /dev/null +++ b/greataped/components/contracts/access_control.go @@ -0,0 +1,77 @@ +package contracts + +import . "github.com/xeronith/diamante/contracts/security" + +var AccessControlPassThroughFilter = func(IAccessControl) bool { return true } + +type ( + AccessControls []IAccessControl + AccessControlIterator func(IAccessControl) + AccessControlCondition func(IAccessControl) bool + AccessControlFilterPredicate func(IAccessControl) bool + AccessControlMapPredicate func(IAccessControl) IAccessControl + AccessControlCacheCallback func() + + IAccessControl interface { + IObject + // Key returns 'Key' of this 'AccessControl' instance. + Key() uint64 + // UpdateKey directly updates 'Key' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateKey(key uint64, editor Identity) + // UpdateKeyAtomic updates 'Key' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateKeyAtomic(transaction ITransaction, key uint64, editor Identity) + // Value returns 'Value' of this 'AccessControl' instance. + Value() uint64 + // UpdateValue directly updates 'Value' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateValue(value uint64, editor Identity) + // UpdateValueAtomic updates 'Value' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateValueAtomic(transaction ITransaction, value uint64, editor Identity) + } + + IAccessControlCollection interface { + Count() int + IsEmpty() bool + IsNotEmpty() bool + HasExactlyOneItem() bool + HasAtLeastOneItem() bool + First() IAccessControl + Append(accessControl IAccessControl) + ForEach(AccessControlIterator) + Array() AccessControls + } + + IAccessControlManager interface { + ISystemComponent + OnCacheChanged(AccessControlCacheCallback) + Count() int + Exists(id int64) bool + ExistsWhich(condition AccessControlCondition) bool + ListAccessControls(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IAccessControlCollection + GetAccessControl(id int64, editor Identity) (IAccessControl, error) + AddOrUpdateAccessControl(key uint64, value uint64, editor Identity) error + AccessControls() map[uint64]uint64 + AddAccessControl(key uint64, value uint64, editor Identity) (IAccessControl, error) + AddAccessControlWithCustomId(id int64, key uint64, value uint64, editor Identity) (IAccessControl, error) + AddAccessControlObject(accessControl IAccessControl, editor Identity) (IAccessControl, error) + AddAccessControlAtomic(transaction ITransaction, key uint64, value uint64, editor Identity) (IAccessControl, error) + AddAccessControlWithCustomIdAtomic(id int64, transaction ITransaction, key uint64, value uint64, editor Identity) (IAccessControl, error) + AddAccessControlObjectAtomic(transaction ITransaction, accessControl IAccessControl, editor Identity) (IAccessControl, error) + Log(key uint64, value uint64, source string, editor Identity, payload string) + UpdateAccessControl(id int64, key uint64, value uint64, editor Identity) (IAccessControl, error) + UpdateAccessControlObject(id int64, accessControl IAccessControl, editor Identity) (IAccessControl, error) + UpdateAccessControlAtomic(transaction ITransaction, id int64, key uint64, value uint64, editor Identity) (IAccessControl, error) + UpdateAccessControlObjectAtomic(transaction ITransaction, id int64, accessControl IAccessControl, editor Identity) (IAccessControl, error) + AddOrUpdateAccessControlObject(id int64, accessControl IAccessControl, editor Identity) (IAccessControl, error) + AddOrUpdateAccessControlObjectAtomic(transaction ITransaction, id int64, accessControl IAccessControl, editor Identity) (IAccessControl, error) + RemoveAccessControl(id int64, editor Identity) (IAccessControl, error) + RemoveAccessControlAtomic(transaction ITransaction, id int64, editor Identity) (IAccessControl, error) + Find(id int64) IAccessControl + ForEach(iterator AccessControlIterator) + Filter(predicate AccessControlFilterPredicate) IAccessControlCollection + Map(predicate AccessControlMapPredicate) IAccessControlCollection + } +) diff --git a/greataped/components/contracts/api.go b/greataped/components/contracts/api.go new file mode 100644 index 0000000..1aa0a71 --- /dev/null +++ b/greataped/components/contracts/api.go @@ -0,0 +1,12 @@ +package contracts + +import . "rail.town/infrastructure/components/api/protobuf" + +type IApi interface { + SetToken(string) + SetDebugMode(bool) + //API Methods + SystemCall(*SystemCallRequest) (*SystemCallResult, error) + Echo(*EchoRequest) (*EchoResult, error) + ResolveError(*ResolveErrorRequest) (*ResolveErrorResult, error) +} diff --git a/greataped/components/contracts/category.go b/greataped/components/contracts/category.go new file mode 100644 index 0000000..58e3746 --- /dev/null +++ b/greataped/components/contracts/category.go @@ -0,0 +1,101 @@ +package contracts + +import . "github.com/xeronith/diamante/contracts/security" + +var CategoryPassThroughFilter = func(ICategory) bool { return true } + +type ( + Categories []ICategory + CategoryIterator func(ICategory) + CategoryCondition func(ICategory) bool + CategoryFilterPredicate func(ICategory) bool + CategoryMapPredicate func(ICategory) ICategory + CategoryCacheCallback func() + + ICategory interface { + IObject + // DependenciesAreUnknown scans all dependencies to make sure a valid parent is set for all of them. + DependenciesAreUnknown() bool + // CategoryTypeId returns parent 'CategoryTypeId' of this 'Category' instance. + CategoryTypeId() int64 + // AssertBelongsToCategoryType checks whether this 'Category' instance is a child of the specified 'CategoryType' + AssertBelongsToCategoryType(categoryType ICategoryType) + // CategoryTypeIsUnknown checks whether a valid parent 'CategoryTypeId' is provided for this 'Category' instance or not. + CategoryTypeIsUnknown() bool + // AssertCategoryTypeIsProvided asserts that a valid 'CategoryTypeId' is provided for this 'Category' instance. A panic will occur if the assertion is not valid. + AssertCategoryTypeIsProvided() + // AssertCategoryType asserts the given 'CategoryTypeId' is in fact the parent of this 'Category' instance. A panic will occur if the assertion is not valid. + AssertCategoryType(categoryTypeId int64) + // CategoryId returns parent 'CategoryId' of this 'Category' instance. + CategoryId() int64 + // AssertBelongsToCategory checks whether this 'Category' instance is a child of the specified 'Category' + AssertBelongsToCategory(category ICategory) + // CategoryIsUnknown checks whether a valid parent 'CategoryId' is provided for this 'Category' instance or not. + CategoryIsUnknown() bool + // AssertCategoryIsProvided asserts that a valid 'CategoryId' is provided for this 'Category' instance. A panic will occur if the assertion is not valid. + AssertCategoryIsProvided() + // AssertCategory asserts the given 'CategoryId' is in fact the parent of this 'Category' instance. A panic will occur if the assertion is not valid. + AssertCategory(categoryId int64) + // Title returns 'Title' of this 'Category' instance. + Title() string + // UpdateTitle directly updates 'Title' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateTitle(title string, editor Identity) + // UpdateTitleAtomic updates 'Title' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateTitleAtomic(transaction ITransaction, title string, editor Identity) + // Description returns 'Description' of this 'Category' instance. + Description() string + // UpdateDescription directly updates 'Description' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateDescription(description string, editor Identity) + // UpdateDescriptionAtomic updates 'Description' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateDescriptionAtomic(transaction ITransaction, description string, editor Identity) + } + + ICategoryCollection interface { + Count() int + IsEmpty() bool + IsNotEmpty() bool + HasExactlyOneItem() bool + HasAtLeastOneItem() bool + First() ICategory + Append(category ICategory) + ForEach(CategoryIterator) + Array() Categories + } + + ICategoryManager interface { + ISystemComponent + OnCacheChanged(CategoryCacheCallback) + Count() int + Exists(id int64) bool + ExistsWhich(condition CategoryCondition) bool + ListCategories(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryCollection + GetCategory(id int64, editor Identity) (ICategory, error) + AddCategory(categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) + AddCategoryWithCustomId(id int64, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) + AddCategoryObject(category ICategory, editor Identity) (ICategory, error) + AddCategoryAtomic(transaction ITransaction, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) + AddCategoryWithCustomIdAtomic(id int64, transaction ITransaction, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) + AddCategoryObjectAtomic(transaction ITransaction, category ICategory, editor Identity) (ICategory, error) + Log(categoryTypeId int64, categoryId int64, title string, description string, source string, editor Identity, payload string) + UpdateCategory(id int64, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) + UpdateCategoryObject(id int64, category ICategory, editor Identity) (ICategory, error) + UpdateCategoryAtomic(transaction ITransaction, id int64, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) + UpdateCategoryObjectAtomic(transaction ITransaction, id int64, category ICategory, editor Identity) (ICategory, error) + AddOrUpdateCategoryObject(id int64, category ICategory, editor Identity) (ICategory, error) + AddOrUpdateCategoryObjectAtomic(transaction ITransaction, id int64, category ICategory, editor Identity) (ICategory, error) + RemoveCategory(id int64, editor Identity) (ICategory, error) + RemoveCategoryAtomic(transaction ITransaction, id int64, editor Identity) (ICategory, error) + Find(id int64) ICategory + ForEach(iterator CategoryIterator) + Filter(predicate CategoryFilterPredicate) ICategoryCollection + Map(predicate CategoryMapPredicate) ICategoryCollection + ListCategoriesByCategoryType(categoryTypeId int64, pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryCollection + ForEachByCategoryType(categoryTypeId int64, iterator CategoryIterator) + ListCategoriesByCategory(categoryId int64, pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryCollection + ForEachByCategory(categoryId int64, iterator CategoryIterator) + } +) diff --git a/greataped/components/contracts/category_type.go b/greataped/components/contracts/category_type.go new file mode 100644 index 0000000..7a2894f --- /dev/null +++ b/greataped/components/contracts/category_type.go @@ -0,0 +1,67 @@ +package contracts + +import . "github.com/xeronith/diamante/contracts/security" + +var CategoryTypePassThroughFilter = func(ICategoryType) bool { return true } + +type ( + CategoryTypes []ICategoryType + CategoryTypeIterator func(ICategoryType) + CategoryTypeCondition func(ICategoryType) bool + CategoryTypeFilterPredicate func(ICategoryType) bool + CategoryTypeMapPredicate func(ICategoryType) ICategoryType + CategoryTypeCacheCallback func() + + ICategoryType interface { + IObject + // Description returns 'Description' of this 'CategoryType' instance. + Description() string + // UpdateDescription directly updates 'Description' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateDescription(description string, editor Identity) + // UpdateDescriptionAtomic updates 'Description' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateDescriptionAtomic(transaction ITransaction, description string, editor Identity) + } + + ICategoryTypeCollection interface { + Count() int + IsEmpty() bool + IsNotEmpty() bool + HasExactlyOneItem() bool + HasAtLeastOneItem() bool + First() ICategoryType + Append(categoryType ICategoryType) + ForEach(CategoryTypeIterator) + Array() CategoryTypes + } + + ICategoryTypeManager interface { + ISystemComponent + OnCacheChanged(CategoryTypeCacheCallback) + Count() int + Exists(id int64) bool + ExistsWhich(condition CategoryTypeCondition) bool + ListCategoryTypes(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryTypeCollection + GetCategoryType(id int64, editor Identity) (ICategoryType, error) + AddCategoryType(description string, editor Identity) (ICategoryType, error) + AddCategoryTypeWithCustomId(id int64, description string, editor Identity) (ICategoryType, error) + AddCategoryTypeObject(categoryType ICategoryType, editor Identity) (ICategoryType, error) + AddCategoryTypeAtomic(transaction ITransaction, description string, editor Identity) (ICategoryType, error) + AddCategoryTypeWithCustomIdAtomic(id int64, transaction ITransaction, description string, editor Identity) (ICategoryType, error) + AddCategoryTypeObjectAtomic(transaction ITransaction, categoryType ICategoryType, editor Identity) (ICategoryType, error) + Log(description string, source string, editor Identity, payload string) + UpdateCategoryType(id int64, description string, editor Identity) (ICategoryType, error) + UpdateCategoryTypeObject(id int64, categoryType ICategoryType, editor Identity) (ICategoryType, error) + UpdateCategoryTypeAtomic(transaction ITransaction, id int64, description string, editor Identity) (ICategoryType, error) + UpdateCategoryTypeObjectAtomic(transaction ITransaction, id int64, categoryType ICategoryType, editor Identity) (ICategoryType, error) + AddOrUpdateCategoryTypeObject(id int64, categoryType ICategoryType, editor Identity) (ICategoryType, error) + AddOrUpdateCategoryTypeObjectAtomic(transaction ITransaction, id int64, categoryType ICategoryType, editor Identity) (ICategoryType, error) + RemoveCategoryType(id int64, editor Identity) (ICategoryType, error) + RemoveCategoryTypeAtomic(transaction ITransaction, id int64, editor Identity) (ICategoryType, error) + Find(id int64) ICategoryType + ForEach(iterator CategoryTypeIterator) + Filter(predicate CategoryTypeFilterPredicate) ICategoryTypeCollection + Map(predicate CategoryTypeMapPredicate) ICategoryTypeCollection + } +) diff --git a/greataped/components/contracts/custom_error.go b/greataped/components/contracts/custom_error.go new file mode 100644 index 0000000..e69f829 --- /dev/null +++ b/greataped/components/contracts/custom_error.go @@ -0,0 +1,62 @@ +package contracts + +import . "github.com/xeronith/diamante/contracts/security" + +var CustomErrorPassThroughFilter = func(ICustomError) bool { return true } + +type ( + CustomErrors []ICustomError + CustomErrorIterator func(ICustomError) + CustomErrorCondition func(ICustomError) bool + CustomErrorFilterPredicate func(ICustomError) bool + CustomErrorMapPredicate func(ICustomError) ICustomError + CustomErrorCacheCallback func() + + ICustomError interface { + } + + ICustomErrorCollection interface { + Count() int + IsEmpty() bool + IsNotEmpty() bool + HasExactlyOneItem() bool + HasAtLeastOneItem() bool + First() ICustomError + Append(customError ICustomError) + ForEach(CustomErrorIterator) + Array() CustomErrors + } + + ICustomErrorManager interface { + ISystemComponent + OnCacheChanged(CustomErrorCacheCallback) + Count() int + Exists(id int64) bool + ExistsWhich(condition CustomErrorCondition) bool + ListCustomErrors(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICustomErrorCollection + GetCustomError(id int64, editor Identity) (ICustomError, error) + AddCustomError(editor Identity) (ICustomError, error) + AddCustomErrorWithCustomId(id int64, editor Identity) (ICustomError, error) + AddCustomErrorObject(customError ICustomError, editor Identity) (ICustomError, error) + AddCustomErrorAtomic(transaction ITransaction, editor Identity) (ICustomError, error) + AddCustomErrorWithCustomIdAtomic(id int64, transaction ITransaction, editor Identity) (ICustomError, error) + AddCustomErrorObjectAtomic(transaction ITransaction, customError ICustomError, editor Identity) (ICustomError, error) + Log(source string, editor Identity, payload string) + UpdateCustomError(id int64, editor Identity) (ICustomError, error) + UpdateCustomErrorObject(id int64, customError ICustomError, editor Identity) (ICustomError, error) + UpdateCustomErrorAtomic(transaction ITransaction, id int64, editor Identity) (ICustomError, error) + UpdateCustomErrorObjectAtomic(transaction ITransaction, id int64, customError ICustomError, editor Identity) (ICustomError, error) + AddOrUpdateCustomErrorObject(id int64, customError ICustomError, editor Identity) (ICustomError, error) + AddOrUpdateCustomErrorObjectAtomic(transaction ITransaction, id int64, customError ICustomError, editor Identity) (ICustomError, error) + RemoveCustomError(id int64, editor Identity) (ICustomError, error) + RemoveCustomErrorAtomic(transaction ITransaction, id int64, editor Identity) (ICustomError, error) + Find(id int64) ICustomError + ForEach(iterator CustomErrorIterator) + Filter(predicate CustomErrorFilterPredicate) ICustomErrorCollection + Map(predicate CustomErrorMapPredicate) ICustomErrorCollection + ResolveError(document IDocument, editor Identity) (IResolveErrorResult, error) + } + + IResolveErrorResult interface { + } +) diff --git a/greataped/components/contracts/document.go b/greataped/components/contracts/document.go new file mode 100644 index 0000000..54dbf33 --- /dev/null +++ b/greataped/components/contracts/document.go @@ -0,0 +1,67 @@ +package contracts + +import . "github.com/xeronith/diamante/contracts/security" + +var DocumentPassThroughFilter = func(IDocument) bool { return true } + +type ( + Documents []IDocument + DocumentIterator func(IDocument) + DocumentCondition func(IDocument) bool + DocumentFilterPredicate func(IDocument) bool + DocumentMapPredicate func(IDocument) IDocument + DocumentCacheCallback func() + + IDocument interface { + IObject + // Content returns 'Content' of this 'Document' instance. + Content() string + // UpdateContent directly updates 'Content' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateContent(content string, editor Identity) + // UpdateContentAtomic updates 'Content' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateContentAtomic(transaction ITransaction, content string, editor Identity) + } + + IDocumentCollection interface { + Count() int + IsEmpty() bool + IsNotEmpty() bool + HasExactlyOneItem() bool + HasAtLeastOneItem() bool + First() IDocument + Append(document IDocument) + ForEach(DocumentIterator) + Array() Documents + } + + IDocumentManager interface { + ISystemComponent + OnCacheChanged(DocumentCacheCallback) + Count() int + Exists(id int64) bool + ExistsWhich(condition DocumentCondition) bool + ListDocuments(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IDocumentCollection + GetDocument(id int64, editor Identity) (IDocument, error) + AddDocument(content string, editor Identity) (IDocument, error) + AddDocumentWithCustomId(id int64, content string, editor Identity) (IDocument, error) + AddDocumentObject(document IDocument, editor Identity) (IDocument, error) + AddDocumentAtomic(transaction ITransaction, content string, editor Identity) (IDocument, error) + AddDocumentWithCustomIdAtomic(id int64, transaction ITransaction, content string, editor Identity) (IDocument, error) + AddDocumentObjectAtomic(transaction ITransaction, document IDocument, editor Identity) (IDocument, error) + Log(content string, source string, editor Identity, payload string) + UpdateDocument(id int64, content string, editor Identity) (IDocument, error) + UpdateDocumentObject(id int64, document IDocument, editor Identity) (IDocument, error) + UpdateDocumentAtomic(transaction ITransaction, id int64, content string, editor Identity) (IDocument, error) + UpdateDocumentObjectAtomic(transaction ITransaction, id int64, document IDocument, editor Identity) (IDocument, error) + AddOrUpdateDocumentObject(id int64, document IDocument, editor Identity) (IDocument, error) + AddOrUpdateDocumentObjectAtomic(transaction ITransaction, id int64, document IDocument, editor Identity) (IDocument, error) + RemoveDocument(id int64, editor Identity) (IDocument, error) + RemoveDocumentAtomic(transaction ITransaction, id int64, editor Identity) (IDocument, error) + Find(id int64) IDocument + ForEach(iterator DocumentIterator) + Filter(predicate DocumentFilterPredicate) IDocumentCollection + Map(predicate DocumentMapPredicate) IDocumentCollection + } +) diff --git a/greataped/components/contracts/identity.go b/greataped/components/contracts/identity.go new file mode 100644 index 0000000..5dde6d3 --- /dev/null +++ b/greataped/components/contracts/identity.go @@ -0,0 +1,249 @@ +package contracts + +import ( + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/system" +) + +var IdentityPassThroughFilter = func(IIdentity) bool { return true } + +type ( + Identities []IIdentity + IdentityIterator func(IIdentity) + IdentityCondition func(IIdentity) bool + IdentityFilterPredicate func(IIdentity) bool + IdentityMapPredicate func(IIdentity) IIdentity + IdentityCacheCallback func() + + IIdentity interface { + IObject + // Username returns 'Username' of this 'Identity' instance. + Username() string + // UpdateUsername directly updates 'Username' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateUsername(username string, editor Identity) + // UpdateUsernameAtomic updates 'Username' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateUsernameAtomic(transaction ITransaction, username string, editor Identity) + // PhoneNumber returns 'PhoneNumber' of this 'Identity' instance. + PhoneNumber() string + // UpdatePhoneNumber directly updates 'PhoneNumber' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdatePhoneNumber(phoneNumber string, editor Identity) + // UpdatePhoneNumberAtomic updates 'PhoneNumber' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdatePhoneNumberAtomic(transaction ITransaction, phoneNumber string, editor Identity) + // PhoneNumberConfirmed returns 'PhoneNumberConfirmed' of this 'Identity' instance. + PhoneNumberConfirmed() bool + // UpdatePhoneNumberConfirmed directly updates 'PhoneNumberConfirmed' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdatePhoneNumberConfirmed(phoneNumberConfirmed bool, editor Identity) + // UpdatePhoneNumberConfirmedAtomic updates 'PhoneNumberConfirmed' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdatePhoneNumberConfirmedAtomic(transaction ITransaction, phoneNumberConfirmed bool, editor Identity) + // FirstName returns 'FirstName' of this 'Identity' instance. + FirstName() string + // UpdateFirstName directly updates 'FirstName' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateFirstName(firstName string, editor Identity) + // UpdateFirstNameAtomic updates 'FirstName' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateFirstNameAtomic(transaction ITransaction, firstName string, editor Identity) + // LastName returns 'LastName' of this 'Identity' instance. + LastName() string + // UpdateLastName directly updates 'LastName' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateLastName(lastName string, editor Identity) + // UpdateLastNameAtomic updates 'LastName' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateLastNameAtomic(transaction ITransaction, lastName string, editor Identity) + // DisplayName returns 'DisplayName' of this 'Identity' instance. + DisplayName() string + // UpdateDisplayName directly updates 'DisplayName' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateDisplayName(displayName string, editor Identity) + // UpdateDisplayNameAtomic updates 'DisplayName' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateDisplayNameAtomic(transaction ITransaction, displayName string, editor Identity) + // Email returns 'Email' of this 'Identity' instance. + Email() string + // UpdateEmail directly updates 'Email' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateEmail(email string, editor Identity) + // UpdateEmailAtomic updates 'Email' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateEmailAtomic(transaction ITransaction, email string, editor Identity) + // EmailConfirmed returns 'EmailConfirmed' of this 'Identity' instance. + EmailConfirmed() bool + // UpdateEmailConfirmed directly updates 'EmailConfirmed' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateEmailConfirmed(emailConfirmed bool, editor Identity) + // UpdateEmailConfirmedAtomic updates 'EmailConfirmed' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateEmailConfirmedAtomic(transaction ITransaction, emailConfirmed bool, editor Identity) + // Avatar returns 'Avatar' of this 'Identity' instance. + Avatar() string + // UpdateAvatar directly updates 'Avatar' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateAvatar(avatar string, editor Identity) + // UpdateAvatarAtomic updates 'Avatar' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateAvatarAtomic(transaction ITransaction, avatar string, editor Identity) + // Banner returns 'Banner' of this 'Identity' instance. + Banner() string + // UpdateBanner directly updates 'Banner' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateBanner(banner string, editor Identity) + // UpdateBannerAtomic updates 'Banner' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateBannerAtomic(transaction ITransaction, banner string, editor Identity) + // Summary returns 'Summary' of this 'Identity' instance. + Summary() string + // UpdateSummary directly updates 'Summary' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateSummary(summary string, editor Identity) + // UpdateSummaryAtomic updates 'Summary' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateSummaryAtomic(transaction ITransaction, summary string, editor Identity) + // Token returns 'Token' of this 'Identity' instance. + Token() string + // UpdateToken directly updates 'Token' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateToken(token string, editor Identity) + // UpdateTokenAtomic updates 'Token' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateTokenAtomic(transaction ITransaction, token string, editor Identity) + // MultiFactor returns 'MultiFactor' of this 'Identity' instance. + MultiFactor() bool + // UpdateMultiFactor directly updates 'MultiFactor' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateMultiFactor(multiFactor bool, editor Identity) + // UpdateMultiFactorAtomic updates 'MultiFactor' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateMultiFactorAtomic(transaction ITransaction, multiFactor bool, editor Identity) + // Hash returns 'Hash' of this 'Identity' instance. + Hash() string + // UpdateHash directly updates 'Hash' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateHash(hash string, editor Identity) + // UpdateHashAtomic updates 'Hash' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateHashAtomic(transaction ITransaction, hash string, editor Identity) + // Salt returns 'Salt' of this 'Identity' instance. + Salt() string + // UpdateSalt directly updates 'Salt' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateSalt(salt string, editor Identity) + // UpdateSaltAtomic updates 'Salt' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateSaltAtomic(transaction ITransaction, salt string, editor Identity) + // PublicKey returns 'PublicKey' of this 'Identity' instance. + PublicKey() string + // UpdatePublicKey directly updates 'PublicKey' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdatePublicKey(publicKey string, editor Identity) + // UpdatePublicKeyAtomic updates 'PublicKey' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdatePublicKeyAtomic(transaction ITransaction, publicKey string, editor Identity) + // PrivateKey returns 'PrivateKey' of this 'Identity' instance. + PrivateKey() string + // UpdatePrivateKey directly updates 'PrivateKey' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdatePrivateKey(privateKey string, editor Identity) + // UpdatePrivateKeyAtomic updates 'PrivateKey' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdatePrivateKeyAtomic(transaction ITransaction, privateKey string, editor Identity) + // Permission returns 'Permission' of this 'Identity' instance. + Permission() uint64 + // UpdatePermission directly updates 'Permission' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdatePermission(permission uint64, editor Identity) + // UpdatePermissionAtomic updates 'Permission' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdatePermissionAtomic(transaction ITransaction, permission uint64, editor Identity) + // Restriction returns 'Restriction' of this 'Identity' instance. + Restriction() uint32 + // UpdateRestriction directly updates 'Restriction' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateRestriction(restriction uint32, editor Identity) + // UpdateRestrictionAtomic updates 'Restriction' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateRestrictionAtomic(transaction ITransaction, restriction uint32, editor Identity) + // LastLogin returns 'LastLogin' of this 'Identity' instance. + LastLogin() int64 + // UpdateLastLogin directly updates 'LastLogin' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateLastLogin(lastLogin int64, editor Identity) + // UpdateLastLoginAtomic updates 'LastLogin' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateLastLoginAtomic(transaction ITransaction, lastLogin int64, editor Identity) + // LoginCount returns 'LoginCount' of this 'Identity' instance. + LoginCount() uint32 + // UpdateLoginCount directly updates 'LoginCount' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateLoginCount(loginCount uint32, editor Identity) + // UpdateLoginCountAtomic updates 'LoginCount' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateLoginCountAtomic(transaction ITransaction, loginCount uint32, editor Identity) + // RemoteAddress returns 'RemoteAddress' of this 'Identity' instance. + RemoteAddress() string + // SetRemoteAddress sets 'RemoteAddress' in-memory value of this 'Identity' instance. + // This doesn't affect the persistent data store. + SetRemoteAddress(remoteAddress string) + // UserAgent returns 'UserAgent' of this 'Identity' instance. + UserAgent() string + // SetUserAgent sets 'UserAgent' in-memory value of this 'Identity' instance. + // This doesn't affect the persistent data store. + SetUserAgent(userAgent string) + Role() Role + IsInRole(role Role) bool + IsRestricted() bool + IsNotRestricted() bool + Payload() Pointer + SetToken(token string) + SetSystemCallHandler(func(Identity, []string) error) + SystemCall(Identity, []string) error + } + + IIdentityCollection interface { + Count() int + IsEmpty() bool + IsNotEmpty() bool + HasExactlyOneItem() bool + HasAtLeastOneItem() bool + First() IIdentity + Append(identity IIdentity) + ForEach(IdentityIterator) + Array() Identities + } + + IIdentityManager interface { + ISystemComponent + ISecurityHandler + OnCacheChanged(IdentityCacheCallback) + Count() int + Exists(id int64) bool + ExistsWhich(condition IdentityCondition) bool + ListIdentities(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IIdentityCollection + GetIdentity(id int64, editor Identity) (IIdentity, error) + AddIdentity(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) + AddIdentityWithCustomId(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) + AddIdentityObject(identity IIdentity, editor Identity) (IIdentity, error) + AddIdentityAtomic(transaction ITransaction, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) + AddIdentityWithCustomIdAtomic(id int64, transaction ITransaction, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) + AddIdentityObjectAtomic(transaction ITransaction, identity IIdentity, editor Identity) (IIdentity, error) + Log(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, source string, editor Identity, payload string) + UpdateIdentity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) + UpdateIdentityObject(id int64, identity IIdentity, editor Identity) (IIdentity, error) + UpdateIdentityAtomic(transaction ITransaction, id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) + UpdateIdentityObjectAtomic(transaction ITransaction, id int64, identity IIdentity, editor Identity) (IIdentity, error) + AddOrUpdateIdentityObject(id int64, identity IIdentity, editor Identity) (IIdentity, error) + AddOrUpdateIdentityObjectAtomic(transaction ITransaction, id int64, identity IIdentity, editor Identity) (IIdentity, error) + RemoveIdentity(id int64, editor Identity) (IIdentity, error) + RemoveIdentityAtomic(transaction ITransaction, id int64, editor Identity) (IIdentity, error) + Find(id int64) IIdentity + ForEach(iterator IdentityIterator) + Filter(predicate IdentityFilterPredicate) IIdentityCollection + Map(predicate IdentityMapPredicate) IIdentityCollection + } +) diff --git a/greataped/components/contracts/model/access_control.go b/greataped/components/contracts/model/access_control.go new file mode 100644 index 0000000..b8960ae --- /dev/null +++ b/greataped/components/contracts/model/access_control.go @@ -0,0 +1,32 @@ +package model + +type ( + AccessControlEntities []IAccessControlEntity + + IAccessControlEntity interface { + IEntity + Key() uint64 + Value() uint64 + } + + IAccessControlPipeEntity interface { + IAccessControlEntity + IPipeEntity + } + + IAccessControlsRepository interface { + IRepository + Add(entity IAccessControlEntity, editor int64) error + AddAtomic(transaction IRepositoryTransaction, entity IAccessControlEntity, editor int64) error + FetchById(editor int64) (IAccessControlEntity, error) + Update(entity IAccessControlEntity, editor int64) error + UpdateAtomic(transaction IRepositoryTransaction, entity IAccessControlEntity, editor int64) error + Remove(entity IAccessControlEntity, editor int64) error + RemoveAtomic(transaction IRepositoryTransaction, entity IAccessControlEntity, editor int64) error + FetchAll() (AccessControlEntities, error) + UpdateKey(id int64, value uint64, editor int64) error + UpdateKeyAtomic(transaction IRepositoryTransaction, id int64, value uint64, editor int64) error + UpdateValue(id int64, value uint64, editor int64) error + UpdateValueAtomic(transaction IRepositoryTransaction, id int64, value uint64, editor int64) error + } +) diff --git a/greataped/components/contracts/model/category.go b/greataped/components/contracts/model/category.go new file mode 100644 index 0000000..66823aa --- /dev/null +++ b/greataped/components/contracts/model/category.go @@ -0,0 +1,36 @@ +package model + +type ( + CategoryEntities []ICategoryEntity + + ICategoryEntity interface { + IEntity + CategoryTypeId() int64 + CategoryId() int64 + Title() string + Description() string + } + + ICategoryPipeEntity interface { + ICategoryEntity + IPipeEntity + } + + ICategoriesRepository interface { + IRepository + Add(entity ICategoryEntity, editor int64) error + AddAtomic(transaction IRepositoryTransaction, entity ICategoryEntity, editor int64) error + FetchById(editor int64) (ICategoryEntity, error) + Update(entity ICategoryEntity, editor int64) error + UpdateAtomic(transaction IRepositoryTransaction, entity ICategoryEntity, editor int64) error + Remove(entity ICategoryEntity, editor int64) error + RemoveAtomic(transaction IRepositoryTransaction, entity ICategoryEntity, editor int64) error + FetchAll() (CategoryEntities, error) + FetchAllByCategoryType(categoryTypeId int64) (CategoryEntities, error) + FetchAllByCategory(categoryId int64) (CategoryEntities, error) + UpdateTitle(id int64, value string, editor int64) error + UpdateTitleAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateDescription(id int64, value string, editor int64) error + UpdateDescriptionAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + } +) diff --git a/greataped/components/contracts/model/category_type.go b/greataped/components/contracts/model/category_type.go new file mode 100644 index 0000000..68c4d0f --- /dev/null +++ b/greataped/components/contracts/model/category_type.go @@ -0,0 +1,29 @@ +package model + +type ( + CategoryTypeEntities []ICategoryTypeEntity + + ICategoryTypeEntity interface { + IEntity + Description() string + } + + ICategoryTypePipeEntity interface { + ICategoryTypeEntity + IPipeEntity + } + + ICategoryTypesRepository interface { + IRepository + Add(entity ICategoryTypeEntity, editor int64) error + AddAtomic(transaction IRepositoryTransaction, entity ICategoryTypeEntity, editor int64) error + FetchById(editor int64) (ICategoryTypeEntity, error) + Update(entity ICategoryTypeEntity, editor int64) error + UpdateAtomic(transaction IRepositoryTransaction, entity ICategoryTypeEntity, editor int64) error + Remove(entity ICategoryTypeEntity, editor int64) error + RemoveAtomic(transaction IRepositoryTransaction, entity ICategoryTypeEntity, editor int64) error + FetchAll() (CategoryTypeEntities, error) + UpdateDescription(id int64, value string, editor int64) error + UpdateDescriptionAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + } +) diff --git a/greataped/components/contracts/model/document.go b/greataped/components/contracts/model/document.go new file mode 100644 index 0000000..57108e1 --- /dev/null +++ b/greataped/components/contracts/model/document.go @@ -0,0 +1,29 @@ +package model + +type ( + DocumentEntities []IDocumentEntity + + IDocumentEntity interface { + IEntity + Content() string + } + + IDocumentPipeEntity interface { + IDocumentEntity + IPipeEntity + } + + IDocumentsRepository interface { + IRepository + Add(entity IDocumentEntity, editor int64) error + AddAtomic(transaction IRepositoryTransaction, entity IDocumentEntity, editor int64) error + FetchById(editor int64) (IDocumentEntity, error) + Update(entity IDocumentEntity, editor int64) error + UpdateAtomic(transaction IRepositoryTransaction, entity IDocumentEntity, editor int64) error + Remove(entity IDocumentEntity, editor int64) error + RemoveAtomic(transaction IRepositoryTransaction, entity IDocumentEntity, editor int64) error + FetchAll() (DocumentEntities, error) + UpdateContent(id int64, value string, editor int64) error + UpdateContentAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + } +) diff --git a/greataped/components/contracts/model/entity.go b/greataped/components/contracts/model/entity.go new file mode 100644 index 0000000..922d3bc --- /dev/null +++ b/greataped/components/contracts/model/entity.go @@ -0,0 +1,32 @@ +package model + +import ( + . "fmt" + + . "github.com/xeronith/diamante/contracts/database" + . "github.com/xeronith/diamante/contracts/system" +) + +type ( + RepositoryTransactionHandler func(transaction IRepositoryTransaction) error + + IEntity interface { + Stringer + Id() int64 + Payload() string + SetPayload(string) + Validate() error + } + + IRepository interface { + Name() string + Migrate() error + GetSqlDatabase() ISqlDatabase + SetSqlDatabase(ISqlDatabase) + Serialize(Pointer, error) + } + + IRepositoryTransaction interface { + OnCommit(func()) + } +) diff --git a/greataped/components/contracts/model/identity.go b/greataped/components/contracts/model/identity.go new file mode 100644 index 0000000..a7e9f88 --- /dev/null +++ b/greataped/components/contracts/model/identity.go @@ -0,0 +1,89 @@ +package model + +type ( + IdentityEntities []IIdentityEntity + + IIdentityEntity interface { + IEntity + Username() string + PhoneNumber() string + PhoneNumberConfirmed() bool + FirstName() string + LastName() string + DisplayName() string + Email() string + EmailConfirmed() bool + Avatar() string + Banner() string + Summary() string + Token() string + MultiFactor() bool + Hash() string + Salt() string + PublicKey() string + PrivateKey() string + Permission() uint64 + Restriction() uint32 + LastLogin() int64 + LoginCount() uint32 + } + + IIdentityPipeEntity interface { + IIdentityEntity + IPipeEntity + } + + IIdentitiesRepository interface { + IRepository + Add(entity IIdentityEntity, editor int64) error + AddAtomic(transaction IRepositoryTransaction, entity IIdentityEntity, editor int64) error + FetchById(editor int64) (IIdentityEntity, error) + Update(entity IIdentityEntity, editor int64) error + UpdateAtomic(transaction IRepositoryTransaction, entity IIdentityEntity, editor int64) error + Remove(entity IIdentityEntity, editor int64) error + RemoveAtomic(transaction IRepositoryTransaction, entity IIdentityEntity, editor int64) error + FetchAll() (IdentityEntities, error) + UpdateUsername(id int64, value string, editor int64) error + UpdateUsernameAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdatePhoneNumber(id int64, value string, editor int64) error + UpdatePhoneNumberAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdatePhoneNumberConfirmed(id int64, value bool, editor int64) error + UpdatePhoneNumberConfirmedAtomic(transaction IRepositoryTransaction, id int64, value bool, editor int64) error + UpdateFirstName(id int64, value string, editor int64) error + UpdateFirstNameAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateLastName(id int64, value string, editor int64) error + UpdateLastNameAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateDisplayName(id int64, value string, editor int64) error + UpdateDisplayNameAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateEmail(id int64, value string, editor int64) error + UpdateEmailAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateEmailConfirmed(id int64, value bool, editor int64) error + UpdateEmailConfirmedAtomic(transaction IRepositoryTransaction, id int64, value bool, editor int64) error + UpdateAvatar(id int64, value string, editor int64) error + UpdateAvatarAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateBanner(id int64, value string, editor int64) error + UpdateBannerAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateSummary(id int64, value string, editor int64) error + UpdateSummaryAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateToken(id int64, value string, editor int64) error + UpdateTokenAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateMultiFactor(id int64, value bool, editor int64) error + UpdateMultiFactorAtomic(transaction IRepositoryTransaction, id int64, value bool, editor int64) error + UpdateHash(id int64, value string, editor int64) error + UpdateHashAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateSalt(id int64, value string, editor int64) error + UpdateSaltAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdatePublicKey(id int64, value string, editor int64) error + UpdatePublicKeyAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdatePrivateKey(id int64, value string, editor int64) error + UpdatePrivateKeyAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdatePermission(id int64, value uint64, editor int64) error + UpdatePermissionAtomic(transaction IRepositoryTransaction, id int64, value uint64, editor int64) error + UpdateRestriction(id int64, value uint32, editor int64) error + UpdateRestrictionAtomic(transaction IRepositoryTransaction, id int64, value uint32, editor int64) error + UpdateLastLogin(id int64, value int64, editor int64) error + UpdateLastLoginAtomic(transaction IRepositoryTransaction, id int64, value int64, editor int64) error + UpdateLoginCount(id int64, value uint32, editor int64) error + UpdateLoginCountAtomic(transaction IRepositoryTransaction, id int64, value uint32, editor int64) error + } +) diff --git a/greataped/components/contracts/model/pipe.go b/greataped/components/contracts/model/pipe.go new file mode 100644 index 0000000..9cc2e69 --- /dev/null +++ b/greataped/components/contracts/model/pipe.go @@ -0,0 +1,47 @@ +package model + +import ( + "sync" + . "time" +) + +// noinspection GoUnusedConst,GoSnakeCaseUsage +const ( + PIPE_DOCUMENT = 0x00000001 + PIPE_SYSTEM_SCHEDULE = 0x00000002 + PIPE_IDENTITY = 0x00000003 + PIPE_ACCESS_CONTROL = 0x00000004 + PIPE_REMOTE_ACTIVITY = 0x00000005 + PIPE_CATEGORY_TYPE = 0x00000006 + PIPE_CATEGORY = 0x00000007 + PIPE_USER = 0x00000008 +) + +type ( + Parameters []interface{} + + IPipe interface { + Input() chan IPipeEntity + Signal() chan int + OpenValve() + } + + IPipeDescriptor interface { + Id() int + GetSemaphore() *sync.Mutex + GetQuery() string + GetParametersResolver() func(IPipeEntity) Parameters + } + + IPipeEntity interface { + GetPipe() int + GetSource() string + GetEditor() int64 + GetQueueTimestamp() Time + } + + IPipeRepository interface { + IRepository + Insert(...IPipeEntity) + } +) diff --git a/greataped/components/contracts/model/remote_activity.go b/greataped/components/contracts/model/remote_activity.go new file mode 100644 index 0000000..3326605 --- /dev/null +++ b/greataped/components/contracts/model/remote_activity.go @@ -0,0 +1,50 @@ +package model + +type ( + RemoteActivityEntities []IRemoteActivityEntity + + IRemoteActivityEntity interface { + IEntity + EntryPoint() string + Duration() int64 + Successful() bool + ErrorMessage() string + RemoteAddress() string + UserAgent() string + EventType() uint32 + Timestamp() int64 + } + + IRemoteActivityPipeEntity interface { + IRemoteActivityEntity + IPipeEntity + } + + IRemoteActivitiesRepository interface { + IRepository + Add(entity IRemoteActivityEntity, editor int64) error + AddAtomic(transaction IRepositoryTransaction, entity IRemoteActivityEntity, editor int64) error + FetchById(editor int64) (IRemoteActivityEntity, error) + Update(entity IRemoteActivityEntity, editor int64) error + UpdateAtomic(transaction IRepositoryTransaction, entity IRemoteActivityEntity, editor int64) error + Remove(entity IRemoteActivityEntity, editor int64) error + RemoveAtomic(transaction IRepositoryTransaction, entity IRemoteActivityEntity, editor int64) error + FetchAll() (RemoteActivityEntities, error) + UpdateEntryPoint(id int64, value string, editor int64) error + UpdateEntryPointAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateDuration(id int64, value int64, editor int64) error + UpdateDurationAtomic(transaction IRepositoryTransaction, id int64, value int64, editor int64) error + UpdateSuccessful(id int64, value bool, editor int64) error + UpdateSuccessfulAtomic(transaction IRepositoryTransaction, id int64, value bool, editor int64) error + UpdateErrorMessage(id int64, value string, editor int64) error + UpdateErrorMessageAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateRemoteAddress(id int64, value string, editor int64) error + UpdateRemoteAddressAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateUserAgent(id int64, value string, editor int64) error + UpdateUserAgentAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + UpdateEventType(id int64, value uint32, editor int64) error + UpdateEventTypeAtomic(transaction IRepositoryTransaction, id int64, value uint32, editor int64) error + UpdateTimestamp(id int64, value int64, editor int64) error + UpdateTimestampAtomic(transaction IRepositoryTransaction, id int64, value int64, editor int64) error + } +) diff --git a/greataped/components/contracts/model/system_schedule.go b/greataped/components/contracts/model/system_schedule.go new file mode 100644 index 0000000..81cced2 --- /dev/null +++ b/greataped/components/contracts/model/system_schedule.go @@ -0,0 +1,32 @@ +package model + +type ( + SystemScheduleEntities []ISystemScheduleEntity + + ISystemScheduleEntity interface { + IEntity + Enabled() bool + Config() string + } + + ISystemSchedulePipeEntity interface { + ISystemScheduleEntity + IPipeEntity + } + + ISystemSchedulesRepository interface { + IRepository + Add(entity ISystemScheduleEntity, editor int64) error + AddAtomic(transaction IRepositoryTransaction, entity ISystemScheduleEntity, editor int64) error + FetchById(editor int64) (ISystemScheduleEntity, error) + Update(entity ISystemScheduleEntity, editor int64) error + UpdateAtomic(transaction IRepositoryTransaction, entity ISystemScheduleEntity, editor int64) error + Remove(entity ISystemScheduleEntity, editor int64) error + RemoveAtomic(transaction IRepositoryTransaction, entity ISystemScheduleEntity, editor int64) error + FetchAll() (SystemScheduleEntities, error) + UpdateEnabled(id int64, value bool, editor int64) error + UpdateEnabledAtomic(transaction IRepositoryTransaction, id int64, value bool, editor int64) error + UpdateConfig(id int64, value string, editor int64) error + UpdateConfigAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + } +) diff --git a/greataped/components/contracts/model/user.go b/greataped/components/contracts/model/user.go new file mode 100644 index 0000000..7c04ba2 --- /dev/null +++ b/greataped/components/contracts/model/user.go @@ -0,0 +1,29 @@ +package model + +type ( + UserEntities []IUserEntity + + IUserEntity interface { + IEntity + Github() string + } + + IUserPipeEntity interface { + IUserEntity + IPipeEntity + } + + IUsersRepository interface { + IRepository + Add(entity IUserEntity, editor int64) error + AddAtomic(transaction IRepositoryTransaction, entity IUserEntity, editor int64) error + FetchById(editor int64) (IUserEntity, error) + Update(entity IUserEntity, editor int64) error + UpdateAtomic(transaction IRepositoryTransaction, entity IUserEntity, editor int64) error + Remove(entity IUserEntity, editor int64) error + RemoveAtomic(transaction IRepositoryTransaction, entity IUserEntity, editor int64) error + FetchAll() (UserEntities, error) + UpdateGithub(id int64, value string, editor int64) error + UpdateGithubAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error + } +) diff --git a/greataped/components/contracts/object.go b/greataped/components/contracts/object.go new file mode 100644 index 0000000..fb3735e --- /dev/null +++ b/greataped/components/contracts/object.go @@ -0,0 +1,13 @@ +package contracts + +import . "fmt" + +type IObject interface { + Stringer + // Returns the unique identifier or 'Id' of this object instance. + Id() int64 + // Checks whether the current instance contains any errors. + Validate() error + Lock(context uint64) + Unlock(context uint64) +} diff --git a/greataped/components/contracts/opcodes.go b/greataped/components/contracts/opcodes.go new file mode 100644 index 0000000..dcf4dee --- /dev/null +++ b/greataped/components/contracts/opcodes.go @@ -0,0 +1,26 @@ +package contracts + +import . "github.com/xeronith/diamante/contracts/server" + +// noinspection GoSnakeCaseUsage +const ( + //SystemCallOperation + SYSTEM_CALL_REQUEST = 0x00001000 + SYSTEM_CALL_RESULT = 0xF0001000 + + //EchoOperation + ECHO_REQUEST = 0x0541BD72 + ECHO_RESULT = 0xAB2FF7D4 + + //ResolveErrorOperation + RESOLVE_ERROR_REQUEST = 0x18B1805B + RESOLVE_ERROR_RESULT = 0xEC02BAC4 +) + +var OPCODES = Opcodes{ + 0x00000000: "N/A", + 0x0541BD72: "ECHO", + 0xAB2FF7D4: "Echo", + 0x18B1805B: "RESOLVE_ERROR", + 0xEC02BAC4: "ResolveError", +} diff --git a/greataped/components/contracts/remote_activity.go b/greataped/components/contracts/remote_activity.go new file mode 100644 index 0000000..4fed8c4 --- /dev/null +++ b/greataped/components/contracts/remote_activity.go @@ -0,0 +1,123 @@ +package contracts + +import . "github.com/xeronith/diamante/contracts/security" + +var RemoteActivityPassThroughFilter = func(IRemoteActivity) bool { return true } + +type ( + RemoteActivities []IRemoteActivity + RemoteActivityIterator func(IRemoteActivity) + RemoteActivityCondition func(IRemoteActivity) bool + RemoteActivityFilterPredicate func(IRemoteActivity) bool + RemoteActivityMapPredicate func(IRemoteActivity) IRemoteActivity + RemoteActivityCacheCallback func() + + IRemoteActivity interface { + IObject + // EntryPoint returns 'EntryPoint' of this 'RemoteActivity' instance. + EntryPoint() string + // UpdateEntryPoint directly updates 'EntryPoint' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateEntryPoint(entryPoint string, editor Identity) + // UpdateEntryPointAtomic updates 'EntryPoint' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateEntryPointAtomic(transaction ITransaction, entryPoint string, editor Identity) + // Duration returns 'Duration' of this 'RemoteActivity' instance. + Duration() int64 + // UpdateDuration directly updates 'Duration' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateDuration(duration int64, editor Identity) + // UpdateDurationAtomic updates 'Duration' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateDurationAtomic(transaction ITransaction, duration int64, editor Identity) + // Successful returns 'Successful' of this 'RemoteActivity' instance. + Successful() bool + // UpdateSuccessful directly updates 'Successful' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateSuccessful(successful bool, editor Identity) + // UpdateSuccessfulAtomic updates 'Successful' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateSuccessfulAtomic(transaction ITransaction, successful bool, editor Identity) + // ErrorMessage returns 'ErrorMessage' of this 'RemoteActivity' instance. + ErrorMessage() string + // UpdateErrorMessage directly updates 'ErrorMessage' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateErrorMessage(errorMessage string, editor Identity) + // UpdateErrorMessageAtomic updates 'ErrorMessage' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateErrorMessageAtomic(transaction ITransaction, errorMessage string, editor Identity) + // RemoteAddress returns 'RemoteAddress' of this 'RemoteActivity' instance. + RemoteAddress() string + // UpdateRemoteAddress directly updates 'RemoteAddress' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateRemoteAddress(remoteAddress string, editor Identity) + // UpdateRemoteAddressAtomic updates 'RemoteAddress' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateRemoteAddressAtomic(transaction ITransaction, remoteAddress string, editor Identity) + // UserAgent returns 'UserAgent' of this 'RemoteActivity' instance. + UserAgent() string + // UpdateUserAgent directly updates 'UserAgent' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateUserAgent(userAgent string, editor Identity) + // UpdateUserAgentAtomic updates 'UserAgent' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateUserAgentAtomic(transaction ITransaction, userAgent string, editor Identity) + // EventType returns 'EventType' of this 'RemoteActivity' instance. + EventType() uint32 + // UpdateEventType directly updates 'EventType' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateEventType(eventType uint32, editor Identity) + // UpdateEventTypeAtomic updates 'EventType' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateEventTypeAtomic(transaction ITransaction, eventType uint32, editor Identity) + // Timestamp returns 'Timestamp' of this 'RemoteActivity' instance. + Timestamp() int64 + // UpdateTimestamp directly updates 'Timestamp' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateTimestamp(timestamp int64, editor Identity) + // UpdateTimestampAtomic updates 'Timestamp' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateTimestampAtomic(transaction ITransaction, timestamp int64, editor Identity) + } + + IRemoteActivityCollection interface { + Count() int + IsEmpty() bool + IsNotEmpty() bool + HasExactlyOneItem() bool + HasAtLeastOneItem() bool + First() IRemoteActivity + Append(remoteActivity IRemoteActivity) + ForEach(RemoteActivityIterator) + Array() RemoteActivities + } + + IRemoteActivityManager interface { + ISystemComponent + OnCacheChanged(RemoteActivityCacheCallback) + Count() int + Exists(id int64) bool + ExistsWhich(condition RemoteActivityCondition) bool + ListRemoteActivities(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IRemoteActivityCollection + GetRemoteActivity(id int64, editor Identity) (IRemoteActivity, error) + AddRemoteActivity(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) + AddRemoteActivityWithCustomId(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) + AddRemoteActivityObject(remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) + AddRemoteActivityAtomic(transaction ITransaction, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) + AddRemoteActivityWithCustomIdAtomic(id int64, transaction ITransaction, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) + AddRemoteActivityObjectAtomic(transaction ITransaction, remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) + Log(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, source string, editor Identity, payload string) + UpdateRemoteActivity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) + UpdateRemoteActivityObject(id int64, remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) + UpdateRemoteActivityAtomic(transaction ITransaction, id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) + UpdateRemoteActivityObjectAtomic(transaction ITransaction, id int64, remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) + AddOrUpdateRemoteActivityObject(id int64, remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) + AddOrUpdateRemoteActivityObjectAtomic(transaction ITransaction, id int64, remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) + RemoveRemoteActivity(id int64, editor Identity) (IRemoteActivity, error) + RemoveRemoteActivityAtomic(transaction ITransaction, id int64, editor Identity) (IRemoteActivity, error) + Find(id int64) IRemoteActivity + ForEach(iterator RemoteActivityIterator) + Filter(predicate RemoteActivityFilterPredicate) IRemoteActivityCollection + Map(predicate RemoteActivityMapPredicate) IRemoteActivityCollection + } +) diff --git a/greataped/components/contracts/spi.go b/greataped/components/contracts/spi.go new file mode 100644 index 0000000..22e12c3 --- /dev/null +++ b/greataped/components/contracts/spi.go @@ -0,0 +1,63 @@ +package contracts + +import . "github.com/xeronith/diamante/contracts/security" + +var SpiPassThroughFilter = func(ISpi) bool { return true } + +type ( + Spis []ISpi + SpiIterator func(ISpi) + SpiCondition func(ISpi) bool + SpiFilterPredicate func(ISpi) bool + SpiMapPredicate func(ISpi) ISpi + SpiCacheCallback func() + + ISpi interface { + } + + ISpiCollection interface { + Count() int + IsEmpty() bool + IsNotEmpty() bool + HasExactlyOneItem() bool + HasAtLeastOneItem() bool + First() ISpi + Append(spi ISpi) + ForEach(SpiIterator) + Array() Spis + } + + ISpiManager interface { + ISystemComponent + OnCacheChanged(SpiCacheCallback) + Count() int + Exists(id int64) bool + ExistsWhich(condition SpiCondition) bool + ListSpis(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ISpiCollection + GetSpi(id int64, editor Identity) (ISpi, error) + AddSpi(editor Identity) (ISpi, error) + AddSpiWithCustomId(id int64, editor Identity) (ISpi, error) + AddSpiObject(spi ISpi, editor Identity) (ISpi, error) + AddSpiAtomic(transaction ITransaction, editor Identity) (ISpi, error) + AddSpiWithCustomIdAtomic(id int64, transaction ITransaction, editor Identity) (ISpi, error) + AddSpiObjectAtomic(transaction ITransaction, spi ISpi, editor Identity) (ISpi, error) + Log(source string, editor Identity, payload string) + UpdateSpi(id int64, editor Identity) (ISpi, error) + UpdateSpiObject(id int64, spi ISpi, editor Identity) (ISpi, error) + UpdateSpiAtomic(transaction ITransaction, id int64, editor Identity) (ISpi, error) + UpdateSpiObjectAtomic(transaction ITransaction, id int64, spi ISpi, editor Identity) (ISpi, error) + AddOrUpdateSpiObject(id int64, spi ISpi, editor Identity) (ISpi, error) + AddOrUpdateSpiObjectAtomic(transaction ITransaction, id int64, spi ISpi, editor Identity) (ISpi, error) + RemoveSpi(id int64, editor Identity) (ISpi, error) + RemoveSpiAtomic(transaction ITransaction, id int64, editor Identity) (ISpi, error) + Find(id int64) ISpi + ForEach(iterator SpiIterator) + Filter(predicate SpiFilterPredicate) ISpiCollection + Map(predicate SpiMapPredicate) ISpiCollection + Echo(document IDocument, editor Identity) (IEchoResult, error) + } + + IEchoResult interface { + Document() IDocument + } +) diff --git a/greataped/components/contracts/system_component.go b/greataped/components/contracts/system_component.go new file mode 100644 index 0000000..1db73a3 --- /dev/null +++ b/greataped/components/contracts/system_component.go @@ -0,0 +1,234 @@ +package contracts + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/service" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/system" +) + +const ( + INITIALIZE = 0 + FINALIZE = 100 +) + +type ( + SystemComponentType int + SystemAction func() error + SystemComponentsContainer map[string]ISystemComponent + SystemObjectCache map[int64]ISystemObject + TransactionHandler func(transaction ITransaction) error + + IConductor interface { + Logger() ILogger + Configuration() IConfiguration + Atomic(handler TransactionHandler) error + Schedule(spec string, callback func()) error + GetSystemComponent(name string) ISystemComponent + RequestActivityStream(method, url, keyId, privateKey string, data []byte, output interface{}) error + LogRemoteCall(context IContext, eventType uint32, source string, input, result interface{}, err error) + + // Document + DocumentManager() IDocumentManager + DocumentExists(id int64) bool + ListDocuments(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IDocumentCollection + GetDocument(id int64, editor Identity) (IDocument, error) + AddDocument(content string, editor Identity) (IDocument, error) + AddDocumentAtomic(transaction ITransaction, content string, editor Identity) (IDocument, error) + LogDocument(content string, source string, editor Identity, payload string) + UpdateDocument(id int64, content string, editor Identity) (IDocument, error) + UpdateDocumentAtomic(transaction ITransaction, id int64, content string, editor Identity) (IDocument, error) + RemoveDocument(id int64, editor Identity) (IDocument, error) + RemoveDocumentAtomic(transaction ITransaction, id int64, editor Identity) (IDocument, error) + + // SystemSchedule + SystemScheduleManager() ISystemScheduleManager + SystemScheduleExists(id int64) bool + ListSystemSchedules(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ISystemScheduleCollection + GetSystemSchedule(id int64, editor Identity) (ISystemSchedule, error) + AddSystemSchedule(enabled bool, config string, editor Identity) (ISystemSchedule, error) + AddSystemScheduleAtomic(transaction ITransaction, enabled bool, config string, editor Identity) (ISystemSchedule, error) + LogSystemSchedule(enabled bool, config string, source string, editor Identity, payload string) + UpdateSystemSchedule(id int64, enabled bool, config string, editor Identity) (ISystemSchedule, error) + UpdateSystemScheduleAtomic(transaction ITransaction, id int64, enabled bool, config string, editor Identity) (ISystemSchedule, error) + RemoveSystemSchedule(id int64, editor Identity) (ISystemSchedule, error) + RemoveSystemScheduleAtomic(transaction ITransaction, id int64, editor Identity) (ISystemSchedule, error) + + // Identity + IdentityManager() IIdentityManager + IdentityExists(id int64) bool + ListIdentities(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IIdentityCollection + GetIdentity(id int64, editor Identity) (IIdentity, error) + AddIdentity(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) + AddIdentityAtomic(transaction ITransaction, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) + LogIdentity(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, source string, editor Identity, payload string) + UpdateIdentity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) + UpdateIdentityAtomic(transaction ITransaction, id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) + RemoveIdentity(id int64, editor Identity) (IIdentity, error) + RemoveIdentityAtomic(transaction ITransaction, id int64, editor Identity) (IIdentity, error) + + // AccessControl + AccessControlManager() IAccessControlManager + AccessControlExists(id int64) bool + ListAccessControls(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IAccessControlCollection + GetAccessControl(id int64, editor Identity) (IAccessControl, error) + AddAccessControl(key uint64, value uint64, editor Identity) (IAccessControl, error) + AddAccessControlAtomic(transaction ITransaction, key uint64, value uint64, editor Identity) (IAccessControl, error) + LogAccessControl(key uint64, value uint64, source string, editor Identity, payload string) + UpdateAccessControl(id int64, key uint64, value uint64, editor Identity) (IAccessControl, error) + UpdateAccessControlAtomic(transaction ITransaction, id int64, key uint64, value uint64, editor Identity) (IAccessControl, error) + RemoveAccessControl(id int64, editor Identity) (IAccessControl, error) + RemoveAccessControlAtomic(transaction ITransaction, id int64, editor Identity) (IAccessControl, error) + + // RemoteActivity + RemoteActivityManager() IRemoteActivityManager + RemoteActivityExists(id int64) bool + ListRemoteActivities(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IRemoteActivityCollection + GetRemoteActivity(id int64, editor Identity) (IRemoteActivity, error) + AddRemoteActivity(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) + AddRemoteActivityAtomic(transaction ITransaction, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) + LogRemoteActivity(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, source string, editor Identity, payload string) + UpdateRemoteActivity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) + UpdateRemoteActivityAtomic(transaction ITransaction, id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) + RemoveRemoteActivity(id int64, editor Identity) (IRemoteActivity, error) + RemoveRemoteActivityAtomic(transaction ITransaction, id int64, editor Identity) (IRemoteActivity, error) + + // CategoryType + CategoryTypeManager() ICategoryTypeManager + CategoryTypeExists(id int64) bool + ListCategoryTypes(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryTypeCollection + GetCategoryType(id int64, editor Identity) (ICategoryType, error) + AddCategoryType(description string, editor Identity) (ICategoryType, error) + AddCategoryTypeAtomic(transaction ITransaction, description string, editor Identity) (ICategoryType, error) + LogCategoryType(description string, source string, editor Identity, payload string) + UpdateCategoryType(id int64, description string, editor Identity) (ICategoryType, error) + UpdateCategoryTypeAtomic(transaction ITransaction, id int64, description string, editor Identity) (ICategoryType, error) + RemoveCategoryType(id int64, editor Identity) (ICategoryType, error) + RemoveCategoryTypeAtomic(transaction ITransaction, id int64, editor Identity) (ICategoryType, error) + + // Category + CategoryManager() ICategoryManager + CategoryExists(id int64) bool + ListCategories(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryCollection + GetCategory(id int64, editor Identity) (ICategory, error) + AddCategory(categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) + AddCategoryAtomic(transaction ITransaction, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) + LogCategory(categoryTypeId int64, categoryId int64, title string, description string, source string, editor Identity, payload string) + UpdateCategory(id int64, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) + UpdateCategoryAtomic(transaction ITransaction, id int64, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) + RemoveCategory(id int64, editor Identity) (ICategory, error) + RemoveCategoryAtomic(transaction ITransaction, id int64, editor Identity) (ICategory, error) + ListCategoriesByCategoryType(categoryTypeId int64, pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryCollection + ForEachCategoryByCategoryType(categoryTypeId int64, iterator CategoryIterator) + ListCategoriesByCategory(categoryId int64, pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryCollection + ForEachCategoryByCategory(categoryId int64, iterator CategoryIterator) + + // User + UserManager() IUserManager + UserExists(id int64) bool + ListUsers(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IUserCollection + GetUser(id int64, editor Identity) (IUser, error) + AddUser(identityId int64, github string, editor Identity) (IUser, error) + AddUserAtomic(transaction ITransaction, identityId int64, github string, editor Identity) (IUser, error) + LogUser(identityId int64, github string, source string, editor Identity, payload string) + UpdateUser(id int64, github string, editor Identity) (IUser, error) + UpdateUserAtomic(transaction ITransaction, id int64, github string, editor Identity) (IUser, error) + RemoveUser(id int64, editor Identity) (IUser, error) + RemoveUserAtomic(transaction ITransaction, id int64, editor Identity) (IUser, error) + + // Spi + SpiManager() ISpiManager + SpiExists(id int64) bool + ListSpis(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ISpiCollection + GetSpi(id int64, editor Identity) (ISpi, error) + AddSpi(editor Identity) (ISpi, error) + AddSpiAtomic(transaction ITransaction, editor Identity) (ISpi, error) + LogSpi(source string, editor Identity, payload string) + UpdateSpi(id int64, editor Identity) (ISpi, error) + UpdateSpiAtomic(transaction ITransaction, id int64, editor Identity) (ISpi, error) + RemoveSpi(id int64, editor Identity) (ISpi, error) + RemoveSpiAtomic(transaction ITransaction, id int64, editor Identity) (ISpi, error) + Echo(document IDocument, editor Identity) (IEchoResult, error) + + // CustomError + CustomErrorManager() ICustomErrorManager + CustomErrorExists(id int64) bool + ListCustomErrors(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICustomErrorCollection + GetCustomError(id int64, editor Identity) (ICustomError, error) + AddCustomError(editor Identity) (ICustomError, error) + AddCustomErrorAtomic(transaction ITransaction, editor Identity) (ICustomError, error) + LogCustomError(source string, editor Identity, payload string) + UpdateCustomError(id int64, editor Identity) (ICustomError, error) + UpdateCustomErrorAtomic(transaction ITransaction, id int64, editor Identity) (ICustomError, error) + RemoveCustomError(id int64, editor Identity) (ICustomError, error) + RemoveCustomErrorAtomic(transaction ITransaction, id int64, editor Identity) (ICustomError, error) + ResolveError(document IDocument, editor Identity) (IResolveErrorResult, error) + + NewDocument(id int64, content string) (IDocument, error) + NewSystemSchedule(id int64, enabled bool, config string) (ISystemSchedule, error) + NewIdentity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) (IIdentity, error) + NewAccessControl(id int64, key uint64, value uint64) (IAccessControl, error) + NewRemoteActivity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) (IRemoteActivity, error) + NewCategoryType(id int64, description string) (ICategoryType, error) + NewCategory(id int64, categoryTypeId int64, categoryId int64, title string, description string) (ICategory, error) + NewUser(id int64, github string) (IUser, error) + NewSpi() (ISpi, error) + NewCustomError() (ICustomError, error) + NewEchoResult(document IDocument, ignored interface{}) IEchoResult + NewResolveErrorResult(ignored interface{}) IResolveErrorResult + } + + ISystemComponent interface { + Name() string + ResolveDependencies(dependencies ...ISystemComponent) error + Load() error + Reload() error + IsTestEnvironment() bool + IsDevelopmentEnvironment() bool + IsStagingEnvironment() bool + IsProductionEnvironment() bool + UniqueId() int64 + Logger() ILogger + Async(task func()) + GenerateUUID() string + GenerateSalt() string + GenerateHash(value string, salt string) string + GenerateJwtToken() string + GenerateRSAKeyPair() (string, string, error) + VerifyJwtToken(token string) error + GenerateCode() string + Email(destination string, format string, args ...interface{}) + SMS(destination string, format string, args ...interface{}) + Format(format string, args ...interface{}) string + Match(pattern string, input string) (bool, error) + Error(interface{}) error + } + + ISystemComponentFactory interface { + Create(SystemComponentType, IConfiguration, ILogger, ...ISystemComponent) ISystemComponent + Components() []ISystemComponent + } + + IAssertionResult interface { + Or(error) + } + + ITransaction interface { + OnCommit(func()) + } +) + +// noinspection GoSnakeCaseUsage +const ( + SYSTEM_COMPONENT_DOCUMENT_MANAGER SystemComponentType = 0x00000001 + SYSTEM_COMPONENT_SYSTEM_SCHEDULE_MANAGER SystemComponentType = 0x00000002 + SYSTEM_COMPONENT_IDENTITY_MANAGER SystemComponentType = 0x00000003 + SYSTEM_COMPONENT_ACCESS_CONTROL_MANAGER SystemComponentType = 0x00000004 + SYSTEM_COMPONENT_REMOTE_ACTIVITY_MANAGER SystemComponentType = 0x00000005 + SYSTEM_COMPONENT_CATEGORY_TYPE_MANAGER SystemComponentType = 0x00000006 + SYSTEM_COMPONENT_CATEGORY_MANAGER SystemComponentType = 0x00000007 + SYSTEM_COMPONENT_USER_MANAGER SystemComponentType = 0x00000008 + SYSTEM_COMPONENT_SPI_MANAGER SystemComponentType = 0x00000009 + SYSTEM_COMPONENT_CUSTOM_ERROR_MANAGER SystemComponentType = 0x0000000A +) diff --git a/greataped/components/contracts/system_dispatcher.go b/greataped/components/contracts/system_dispatcher.go new file mode 100644 index 0000000..46a716c --- /dev/null +++ b/greataped/components/contracts/system_dispatcher.go @@ -0,0 +1,731 @@ +package contracts + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" +) + +type IDispatcher interface { + Logger() ILogger + Config() IConfiguration + FQDN() string + PublicUrl() string + Accelerator() IDispatcherCache + // Atomic initializes an atomic context using a new transaction. + Atomic(SystemAction) + // Schedule creates a new recurring event. First parameter is the cron style + // schedule spec with a callback function as second parameter. + Schedule(id int64, spec string, callback func(IDispatcher, string)) error + // Transaction returns the current running transaction. + Transaction() ITransaction + // IdentityManager returns system identity manager. + IdentityManager() IIdentityManager + // Identity returns the current identity. + Identity() Identity + // CurrentUser returns the current user. + CurrentUser() IUser + // SignOut logs the current user out. + SignOut() error + + // Document + // ------------------------------------------------------------ + + // DocumentExists checks whether a specific 'Document' with the provided + // unique identifier or 'Id' exists in the system. + DocumentExists(id int64) bool + // DocumentExistsWhich checks whether a specific 'Document' exists in the system + // which satisfies the provided condition. + DocumentExistsWhich(condition DocumentCondition) bool + // ListDocuments returns a list of all 'Document' instances in the system. + ListDocuments() IDocumentCollection + // ForEachDocument loops over all 'Document' instances in the system running + // the provided iterator for each of them. + ForEachDocument(iterator DocumentIterator) + // FilterDocuments returns a filtered list of 'Document' instances based + // on the provided predicate. + FilterDocuments(predicate DocumentFilterPredicate) IDocumentCollection + // MapDocuments loops over all 'Document' instances in the system and + // returns a transformed list based on the provided predicate. + MapDocuments(predicate DocumentMapPredicate) IDocumentCollection + // GetDocument finds a specific 'Document' instance using + // the provided unique identifier or 'Id'. + GetDocument(id int64) IDocument + // AddDocument creates a new 'Document' instance with an auto-generated unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddDocument(content string) IDocument + // AddDocumentWithCustomId creates a new 'Document' instance with a custom unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddDocumentWithCustomId(id int64, content string) IDocument + // LogDocument creates a new 'Document' instance using the provided property values + // and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is asynchronous. + LogDocument(content string, source string, payload string) + // UpdateDocument finds the 'Document' instance using the provided unique identifier and updates it using + // the provided property values and reflects the changes to persistent data store and system + // cache. The method is smart enough to respect the transaction if used in an x.Atomic context. + // This method is synchronous. + UpdateDocument(id int64, content string) IDocument + // UpdateDocumentObject finds and updates the 'Document' using the provided instance and reflects the + // changes to persistent data store and system cache. The method is smart enough to + // respect the transaction if used in an x.Atomic context. This method is synchronous. + UpdateDocumentObject(object IObject, document IDocument) IDocument + // AddOrUpdateDocumentObject tries to find the 'Document' using the provided instance, then updates it in persistent + // data store and system cache or creates it if doesn't already exist. The method is smart enough + // to respect the transaction if used in an x.Atomic context. This method is synchronous. + AddOrUpdateDocumentObject(object IObject, document IDocument) IDocument + // RemoveDocument finds the 'Document' instance using the provided unique identifier and + // removes it from the system cache. The method is smart enough to respect + // the transaction if used in an x.Atomic context. This method is synchronous. + RemoveDocument(id int64) IDocument + + // SystemSchedule + // ------------------------------------------------------------ + + // SystemScheduleExists checks whether a specific 'System Schedule' with the provided + // unique identifier or 'Id' exists in the system. + SystemScheduleExists(id int64) bool + // SystemScheduleExistsWhich checks whether a specific 'System Schedule' exists in the system + // which satisfies the provided condition. + SystemScheduleExistsWhich(condition SystemScheduleCondition) bool + // ListSystemSchedules returns a list of all 'System Schedule' instances in the system. + ListSystemSchedules() ISystemScheduleCollection + // ForEachSystemSchedule loops over all 'System Schedule' instances in the system running + // the provided iterator for each of them. + ForEachSystemSchedule(iterator SystemScheduleIterator) + // FilterSystemSchedules returns a filtered list of 'System Schedule' instances based + // on the provided predicate. + FilterSystemSchedules(predicate SystemScheduleFilterPredicate) ISystemScheduleCollection + // MapSystemSchedules loops over all 'System Schedule' instances in the system and + // returns a transformed list based on the provided predicate. + MapSystemSchedules(predicate SystemScheduleMapPredicate) ISystemScheduleCollection + // GetSystemSchedule finds a specific 'System Schedule' instance using + // the provided unique identifier or 'Id'. + GetSystemSchedule(id int64) ISystemSchedule + // AddSystemSchedule creates a new 'System Schedule' instance with an auto-generated unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddSystemSchedule(enabled bool, config string) ISystemSchedule + // AddSystemScheduleWithCustomId creates a new 'System Schedule' instance with a custom unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddSystemScheduleWithCustomId(id int64, enabled bool, config string) ISystemSchedule + // LogSystemSchedule creates a new 'System Schedule' instance using the provided property values + // and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is asynchronous. + LogSystemSchedule(enabled bool, config string, source string, payload string) + // UpdateSystemSchedule finds the 'System Schedule' instance using the provided unique identifier and updates it using + // the provided property values and reflects the changes to persistent data store and system + // cache. The method is smart enough to respect the transaction if used in an x.Atomic context. + // This method is synchronous. + UpdateSystemSchedule(id int64, enabled bool, config string) ISystemSchedule + // UpdateSystemScheduleObject finds and updates the 'System Schedule' using the provided instance and reflects the + // changes to persistent data store and system cache. The method is smart enough to + // respect the transaction if used in an x.Atomic context. This method is synchronous. + UpdateSystemScheduleObject(object IObject, systemSchedule ISystemSchedule) ISystemSchedule + // AddOrUpdateSystemScheduleObject tries to find the 'System Schedule' using the provided instance, then updates it in persistent + // data store and system cache or creates it if doesn't already exist. The method is smart enough + // to respect the transaction if used in an x.Atomic context. This method is synchronous. + AddOrUpdateSystemScheduleObject(object IObject, systemSchedule ISystemSchedule) ISystemSchedule + // RemoveSystemSchedule finds the 'System Schedule' instance using the provided unique identifier and + // removes it from the system cache. The method is smart enough to respect + // the transaction if used in an x.Atomic context. This method is synchronous. + RemoveSystemSchedule(id int64) ISystemSchedule + + // Identity + // ------------------------------------------------------------ + + // IdentityExists checks whether a specific 'Identity' with the provided + // unique identifier or 'Id' exists in the system. + IdentityExists(id int64) bool + // IdentityExistsWhich checks whether a specific 'Identity' exists in the system + // which satisfies the provided condition. + IdentityExistsWhich(condition IdentityCondition) bool + // ListIdentities returns a list of all 'Identity' instances in the system. + ListIdentities() IIdentityCollection + // ForEachIdentity loops over all 'Identity' instances in the system running + // the provided iterator for each of them. + ForEachIdentity(iterator IdentityIterator) + // FilterIdentities returns a filtered list of 'Identity' instances based + // on the provided predicate. + FilterIdentities(predicate IdentityFilterPredicate) IIdentityCollection + // MapIdentities loops over all 'Identity' instances in the system and + // returns a transformed list based on the provided predicate. + MapIdentities(predicate IdentityMapPredicate) IIdentityCollection + // GetIdentity finds a specific 'Identity' instance using + // the provided unique identifier or 'Id'. + GetIdentity(id int64) IIdentity + // AddIdentity creates a new 'Identity' instance with an auto-generated unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddIdentity(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) IIdentity + // AddIdentityWithCustomId creates a new 'Identity' instance with a custom unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddIdentityWithCustomId(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) IIdentity + // LogIdentity creates a new 'Identity' instance using the provided property values + // and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is asynchronous. + LogIdentity(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, source string, payload string) + // UpdateIdentity finds the 'Identity' instance using the provided unique identifier and updates it using + // the provided property values and reflects the changes to persistent data store and system + // cache. The method is smart enough to respect the transaction if used in an x.Atomic context. + // This method is synchronous. + UpdateIdentity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) IIdentity + // UpdateIdentityObject finds and updates the 'Identity' using the provided instance and reflects the + // changes to persistent data store and system cache. The method is smart enough to + // respect the transaction if used in an x.Atomic context. This method is synchronous. + UpdateIdentityObject(object IObject, identity IIdentity) IIdentity + // AddOrUpdateIdentityObject tries to find the 'Identity' using the provided instance, then updates it in persistent + // data store and system cache or creates it if doesn't already exist. The method is smart enough + // to respect the transaction if used in an x.Atomic context. This method is synchronous. + AddOrUpdateIdentityObject(object IObject, identity IIdentity) IIdentity + // RemoveIdentity finds the 'Identity' instance using the provided unique identifier and + // removes it from the system cache. The method is smart enough to respect + // the transaction if used in an x.Atomic context. This method is synchronous. + RemoveIdentity(id int64) IIdentity + + // AccessControl + // ------------------------------------------------------------ + + // AccessControlExists checks whether a specific 'Access Control' with the provided + // unique identifier or 'Id' exists in the system. + AccessControlExists(id int64) bool + // AccessControlExistsWhich checks whether a specific 'Access Control' exists in the system + // which satisfies the provided condition. + AccessControlExistsWhich(condition AccessControlCondition) bool + // ListAccessControls returns a list of all 'Access Control' instances in the system. + ListAccessControls() IAccessControlCollection + // ForEachAccessControl loops over all 'Access Control' instances in the system running + // the provided iterator for each of them. + ForEachAccessControl(iterator AccessControlIterator) + // FilterAccessControls returns a filtered list of 'Access Control' instances based + // on the provided predicate. + FilterAccessControls(predicate AccessControlFilterPredicate) IAccessControlCollection + // MapAccessControls loops over all 'Access Control' instances in the system and + // returns a transformed list based on the provided predicate. + MapAccessControls(predicate AccessControlMapPredicate) IAccessControlCollection + // GetAccessControl finds a specific 'Access Control' instance using + // the provided unique identifier or 'Id'. + GetAccessControl(id int64) IAccessControl + // AddAccessControl creates a new 'Access Control' instance with an auto-generated unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddAccessControl(key uint64, value uint64) IAccessControl + // AddAccessControlWithCustomId creates a new 'Access Control' instance with a custom unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddAccessControlWithCustomId(id int64, key uint64, value uint64) IAccessControl + // LogAccessControl creates a new 'Access Control' instance using the provided property values + // and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is asynchronous. + LogAccessControl(key uint64, value uint64, source string, payload string) + // UpdateAccessControl finds the 'Access Control' instance using the provided unique identifier and updates it using + // the provided property values and reflects the changes to persistent data store and system + // cache. The method is smart enough to respect the transaction if used in an x.Atomic context. + // This method is synchronous. + UpdateAccessControl(id int64, key uint64, value uint64) IAccessControl + // UpdateAccessControlObject finds and updates the 'Access Control' using the provided instance and reflects the + // changes to persistent data store and system cache. The method is smart enough to + // respect the transaction if used in an x.Atomic context. This method is synchronous. + UpdateAccessControlObject(object IObject, accessControl IAccessControl) IAccessControl + // AddOrUpdateAccessControlObject tries to find the 'Access Control' using the provided instance, then updates it in persistent + // data store and system cache or creates it if doesn't already exist. The method is smart enough + // to respect the transaction if used in an x.Atomic context. This method is synchronous. + AddOrUpdateAccessControlObject(object IObject, accessControl IAccessControl) IAccessControl + // RemoveAccessControl finds the 'Access Control' instance using the provided unique identifier and + // removes it from the system cache. The method is smart enough to respect + // the transaction if used in an x.Atomic context. This method is synchronous. + RemoveAccessControl(id int64) IAccessControl + + // RemoteActivity + // ------------------------------------------------------------ + + // RemoteActivityExists checks whether a specific 'Remote Activity' with the provided + // unique identifier or 'Id' exists in the system. + RemoteActivityExists(id int64) bool + // RemoteActivityExistsWhich checks whether a specific 'Remote Activity' exists in the system + // which satisfies the provided condition. + RemoteActivityExistsWhich(condition RemoteActivityCondition) bool + // ListRemoteActivities returns a list of all 'Remote Activity' instances in the system. + ListRemoteActivities() IRemoteActivityCollection + // ForEachRemoteActivity loops over all 'Remote Activity' instances in the system running + // the provided iterator for each of them. + ForEachRemoteActivity(iterator RemoteActivityIterator) + // FilterRemoteActivities returns a filtered list of 'Remote Activity' instances based + // on the provided predicate. + FilterRemoteActivities(predicate RemoteActivityFilterPredicate) IRemoteActivityCollection + // MapRemoteActivities loops over all 'Remote Activity' instances in the system and + // returns a transformed list based on the provided predicate. + MapRemoteActivities(predicate RemoteActivityMapPredicate) IRemoteActivityCollection + // GetRemoteActivity finds a specific 'Remote Activity' instance using + // the provided unique identifier or 'Id'. + GetRemoteActivity(id int64) IRemoteActivity + // AddRemoteActivity creates a new 'Remote Activity' instance with an auto-generated unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddRemoteActivity(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) IRemoteActivity + // AddRemoteActivityWithCustomId creates a new 'Remote Activity' instance with a custom unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddRemoteActivityWithCustomId(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) IRemoteActivity + // LogRemoteActivity creates a new 'Remote Activity' instance using the provided property values + // and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is asynchronous. + LogRemoteActivity(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, source string, payload string) + // UpdateRemoteActivity finds the 'Remote Activity' instance using the provided unique identifier and updates it using + // the provided property values and reflects the changes to persistent data store and system + // cache. The method is smart enough to respect the transaction if used in an x.Atomic context. + // This method is synchronous. + UpdateRemoteActivity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) IRemoteActivity + // UpdateRemoteActivityObject finds and updates the 'Remote Activity' using the provided instance and reflects the + // changes to persistent data store and system cache. The method is smart enough to + // respect the transaction if used in an x.Atomic context. This method is synchronous. + UpdateRemoteActivityObject(object IObject, remoteActivity IRemoteActivity) IRemoteActivity + // AddOrUpdateRemoteActivityObject tries to find the 'Remote Activity' using the provided instance, then updates it in persistent + // data store and system cache or creates it if doesn't already exist. The method is smart enough + // to respect the transaction if used in an x.Atomic context. This method is synchronous. + AddOrUpdateRemoteActivityObject(object IObject, remoteActivity IRemoteActivity) IRemoteActivity + // RemoveRemoteActivity finds the 'Remote Activity' instance using the provided unique identifier and + // removes it from the system cache. The method is smart enough to respect + // the transaction if used in an x.Atomic context. This method is synchronous. + RemoveRemoteActivity(id int64) IRemoteActivity + + // CategoryType + // ------------------------------------------------------------ + + // CategoryTypeExists checks whether a specific 'Category Type' with the provided + // unique identifier or 'Id' exists in the system. + CategoryTypeExists(id int64) bool + // CategoryTypeExistsWhich checks whether a specific 'Category Type' exists in the system + // which satisfies the provided condition. + CategoryTypeExistsWhich(condition CategoryTypeCondition) bool + // ListCategoryTypes returns a list of all 'Category Type' instances in the system. + ListCategoryTypes() ICategoryTypeCollection + // ForEachCategoryType loops over all 'Category Type' instances in the system running + // the provided iterator for each of them. + ForEachCategoryType(iterator CategoryTypeIterator) + // FilterCategoryTypes returns a filtered list of 'Category Type' instances based + // on the provided predicate. + FilterCategoryTypes(predicate CategoryTypeFilterPredicate) ICategoryTypeCollection + // MapCategoryTypes loops over all 'Category Type' instances in the system and + // returns a transformed list based on the provided predicate. + MapCategoryTypes(predicate CategoryTypeMapPredicate) ICategoryTypeCollection + // GetCategoryType finds a specific 'Category Type' instance using + // the provided unique identifier or 'Id'. + GetCategoryType(id int64) ICategoryType + // AddCategoryType creates a new 'Category Type' instance with an auto-generated unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddCategoryType(description string) ICategoryType + // AddCategoryTypeWithCustomId creates a new 'Category Type' instance with a custom unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddCategoryTypeWithCustomId(id int64, description string) ICategoryType + // LogCategoryType creates a new 'Category Type' instance using the provided property values + // and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is asynchronous. + LogCategoryType(description string, source string, payload string) + // UpdateCategoryType finds the 'Category Type' instance using the provided unique identifier and updates it using + // the provided property values and reflects the changes to persistent data store and system + // cache. The method is smart enough to respect the transaction if used in an x.Atomic context. + // This method is synchronous. + UpdateCategoryType(id int64, description string) ICategoryType + // UpdateCategoryTypeObject finds and updates the 'Category Type' using the provided instance and reflects the + // changes to persistent data store and system cache. The method is smart enough to + // respect the transaction if used in an x.Atomic context. This method is synchronous. + UpdateCategoryTypeObject(object IObject, categoryType ICategoryType) ICategoryType + // AddOrUpdateCategoryTypeObject tries to find the 'Category Type' using the provided instance, then updates it in persistent + // data store and system cache or creates it if doesn't already exist. The method is smart enough + // to respect the transaction if used in an x.Atomic context. This method is synchronous. + AddOrUpdateCategoryTypeObject(object IObject, categoryType ICategoryType) ICategoryType + // RemoveCategoryType finds the 'Category Type' instance using the provided unique identifier and + // removes it from the system cache. The method is smart enough to respect + // the transaction if used in an x.Atomic context. This method is synchronous. + RemoveCategoryType(id int64) ICategoryType + + // Category + // ------------------------------------------------------------ + + // CategoryExists checks whether a specific 'Category' with the provided + // unique identifier or 'Id' exists in the system. + CategoryExists(id int64) bool + // CategoryExistsWhich checks whether a specific 'Category' exists in the system + // which satisfies the provided condition. + CategoryExistsWhich(condition CategoryCondition) bool + // ListCategories returns a list of all 'Category' instances in the system. + ListCategories() ICategoryCollection + // ForEachCategory loops over all 'Category' instances in the system running + // the provided iterator for each of them. + ForEachCategory(iterator CategoryIterator) + // FilterCategories returns a filtered list of 'Category' instances based + // on the provided predicate. + FilterCategories(predicate CategoryFilterPredicate) ICategoryCollection + // MapCategories loops over all 'Category' instances in the system and + // returns a transformed list based on the provided predicate. + MapCategories(predicate CategoryMapPredicate) ICategoryCollection + // GetCategory finds a specific 'Category' instance using + // the provided unique identifier or 'Id'. + GetCategory(id int64) ICategory + // AddCategory creates a new 'Category' instance with an auto-generated unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddCategory(categoryTypeId int64, categoryId int64, title string, description string) ICategory + // AddCategoryWithCustomId creates a new 'Category' instance with a custom unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddCategoryWithCustomId(id int64, categoryTypeId int64, categoryId int64, title string, description string) ICategory + // LogCategory creates a new 'Category' instance using the provided property values + // and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is asynchronous. + LogCategory(categoryTypeId int64, categoryId int64, title string, description string, source string, payload string) + // UpdateCategory finds the 'Category' instance using the provided unique identifier and updates it using + // the provided property values and reflects the changes to persistent data store and system + // cache. The method is smart enough to respect the transaction if used in an x.Atomic context. + // This method is synchronous. + UpdateCategory(id int64, categoryTypeId int64, categoryId int64, title string, description string) ICategory + // UpdateCategoryObject finds and updates the 'Category' using the provided instance and reflects the + // changes to persistent data store and system cache. The method is smart enough to + // respect the transaction if used in an x.Atomic context. This method is synchronous. + UpdateCategoryObject(object IObject, category ICategory) ICategory + // AddOrUpdateCategoryObject tries to find the 'Category' using the provided instance, then updates it in persistent + // data store and system cache or creates it if doesn't already exist. The method is smart enough + // to respect the transaction if used in an x.Atomic context. This method is synchronous. + AddOrUpdateCategoryObject(object IObject, category ICategory) ICategory + // RemoveCategory finds the 'Category' instance using the provided unique identifier and + // removes it from the system cache. The method is smart enough to respect + // the transaction if used in an x.Atomic context. This method is synchronous. + RemoveCategory(id int64) ICategory + // ListCategoriesByCategoryType returns a list of all 'Category' instances in the system + // that are children of the provided 'Category Type' instance. + ListCategoriesByCategoryType(categoryType ICategoryType) ICategoryCollection + // ListCategoriesByCategoryTypeId returns a list of all 'Category' instances in the system that are + // children of the 'Category Type' instance with the provided unique identifier. + ListCategoriesByCategoryTypeId(categoryTypeId int64) ICategoryCollection + // ForEachCategoryByCategoryType loops over all 'Category' instances in the system that are children + // of the provided 'Category Type' instance, running the provided iterator for each of them. + ForEachCategoryByCategoryType(categoryType ICategoryType, iterator CategoryIterator) + // ForEachCategoryByCategoryTypeId loops over all 'Category' instances in the system that are children + // of the 'Category Type' instance with the provided unique identifier, + // running the provided iterator for each of them. + ForEachCategoryByCategoryTypeId(categoryTypeId int64, iterator CategoryIterator) + // ListCategoriesByCategory returns a list of all 'Category' instances in the system + // that are children of the provided 'Category' instance. + ListCategoriesByCategory(category ICategory) ICategoryCollection + // ListCategoriesByCategoryId returns a list of all 'Category' instances in the system that are + // children of the 'Category' instance with the provided unique identifier. + ListCategoriesByCategoryId(categoryId int64) ICategoryCollection + // ForEachCategoryByCategory loops over all 'Category' instances in the system that are children + // of the provided 'Category' instance, running the provided iterator for each of them. + ForEachCategoryByCategory(category ICategory, iterator CategoryIterator) + // ForEachCategoryByCategoryId loops over all 'Category' instances in the system that are children + // of the 'Category' instance with the provided unique identifier, + // running the provided iterator for each of them. + ForEachCategoryByCategoryId(categoryId int64, iterator CategoryIterator) + + // User + // ------------------------------------------------------------ + + // UserExists checks whether a specific 'User' with the provided + // unique identifier or 'Id' exists in the system. + UserExists(id int64) bool + // UserExistsWhich checks whether a specific 'User' exists in the system + // which satisfies the provided condition. + UserExistsWhich(condition UserCondition) bool + // ListUsers returns a list of all 'User' instances in the system. + ListUsers() IUserCollection + // ForEachUser loops over all 'User' instances in the system running + // the provided iterator for each of them. + ForEachUser(iterator UserIterator) + // FilterUsers returns a filtered list of 'User' instances based + // on the provided predicate. + FilterUsers(predicate UserFilterPredicate) IUserCollection + // MapUsers loops over all 'User' instances in the system and + // returns a transformed list based on the provided predicate. + MapUsers(predicate UserMapPredicate) IUserCollection + // GetUser finds a specific 'User' instance using + // the provided unique identifier or 'Id'. + GetUser(id int64) IUser + // AddUser creates a new 'User' instance with an auto-generated unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddUser(identityId int64, github string) IUser + // AddUserObject creates a new 'User' instance with an auto-generated unique identifier using + // the property values in provided instance and adds it to persistent data store and + // system cache. The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddUserObject(identity IIdentity, user IUser) IUser + // LogUser creates a new 'User' instance using the provided property values + // and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is asynchronous. + LogUser(identityId int64, github string, source string, payload string) + // UpdateUser finds the 'User' instance using the provided unique identifier and updates it using + // the provided property values and reflects the changes to persistent data store and system + // cache. The method is smart enough to respect the transaction if used in an x.Atomic context. + // This method is synchronous. + UpdateUser(id int64, github string) IUser + // UpdateUserObject finds and updates the 'User' using the provided instance and reflects the + // changes to persistent data store and system cache. The method is smart enough to + // respect the transaction if used in an x.Atomic context. This method is synchronous. + UpdateUserObject(object IObject, user IUser) IUser + // AddOrUpdateUserObject tries to find the 'User' using the provided instance, then updates it in persistent + // data store and system cache or creates it if doesn't already exist. The method is smart enough + // to respect the transaction if used in an x.Atomic context. This method is synchronous. + AddOrUpdateUserObject(object IObject, user IUser) IUser + // RemoveUser finds the 'User' instance using the provided unique identifier and + // removes it from the system cache. The method is smart enough to respect + // the transaction if used in an x.Atomic context. This method is synchronous. + RemoveUser(id int64) IUser + + // Spi + // ------------------------------------------------------------ + + // SpiExists checks whether a specific 'Spi' with the provided + // unique identifier or 'Id' exists in the system. + SpiExists(id int64) bool + // SpiExistsWhich checks whether a specific 'Spi' exists in the system + // which satisfies the provided condition. + SpiExistsWhich(condition SpiCondition) bool + // ListSpis returns a list of all 'Spi' instances in the system. + ListSpis() ISpiCollection + // ForEachSpi loops over all 'Spi' instances in the system running + // the provided iterator for each of them. + ForEachSpi(iterator SpiIterator) + // FilterSpis returns a filtered list of 'Spi' instances based + // on the provided predicate. + FilterSpis(predicate SpiFilterPredicate) ISpiCollection + // MapSpis loops over all 'Spi' instances in the system and + // returns a transformed list based on the provided predicate. + MapSpis(predicate SpiMapPredicate) ISpiCollection + // GetSpi finds a specific 'Spi' instance using + // the provided unique identifier or 'Id'. + GetSpi(id int64) ISpi + // AddSpi creates a new 'Spi' instance with an auto-generated unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddSpi() ISpi + // AddSpiWithCustomId creates a new 'Spi' instance with a custom unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddSpiWithCustomId(id int64) ISpi + // LogSpi creates a new 'Spi' instance using the provided property values + // and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is asynchronous. + LogSpi(source string, payload string) + // UpdateSpi finds the 'Spi' instance using the provided unique identifier and updates it using + // the provided property values and reflects the changes to persistent data store and system + // cache. The method is smart enough to respect the transaction if used in an x.Atomic context. + // This method is synchronous. + UpdateSpi(id int64) ISpi + // UpdateSpiObject finds and updates the 'Spi' using the provided instance and reflects the + // changes to persistent data store and system cache. The method is smart enough to + // respect the transaction if used in an x.Atomic context. This method is synchronous. + UpdateSpiObject(object IObject, spi ISpi) ISpi + // AddOrUpdateSpiObject tries to find the 'Spi' using the provided instance, then updates it in persistent + // data store and system cache or creates it if doesn't already exist. The method is smart enough + // to respect the transaction if used in an x.Atomic context. This method is synchronous. + AddOrUpdateSpiObject(object IObject, spi ISpi) ISpi + // RemoveSpi finds the 'Spi' instance using the provided unique identifier and + // removes it from the system cache. The method is smart enough to respect + // the transaction if used in an x.Atomic context. This method is synchronous. + RemoveSpi(id int64) ISpi + Echo(document IDocument) (IEchoResult, error) + + // CustomError + // ------------------------------------------------------------ + + // CustomErrorExists checks whether a specific 'Custom Error' with the provided + // unique identifier or 'Id' exists in the system. + CustomErrorExists(id int64) bool + // CustomErrorExistsWhich checks whether a specific 'Custom Error' exists in the system + // which satisfies the provided condition. + CustomErrorExistsWhich(condition CustomErrorCondition) bool + // ListCustomErrors returns a list of all 'Custom Error' instances in the system. + ListCustomErrors() ICustomErrorCollection + // ForEachCustomError loops over all 'Custom Error' instances in the system running + // the provided iterator for each of them. + ForEachCustomError(iterator CustomErrorIterator) + // FilterCustomErrors returns a filtered list of 'Custom Error' instances based + // on the provided predicate. + FilterCustomErrors(predicate CustomErrorFilterPredicate) ICustomErrorCollection + // MapCustomErrors loops over all 'Custom Error' instances in the system and + // returns a transformed list based on the provided predicate. + MapCustomErrors(predicate CustomErrorMapPredicate) ICustomErrorCollection + // GetCustomError finds a specific 'Custom Error' instance using + // the provided unique identifier or 'Id'. + GetCustomError(id int64) ICustomError + // AddCustomError creates a new 'Custom Error' instance with an auto-generated unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddCustomError() ICustomError + // AddCustomErrorWithCustomId creates a new 'Custom Error' instance with a custom unique identifier using the + // provided property values and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is synchronous. + AddCustomErrorWithCustomId(id int64) ICustomError + // LogCustomError creates a new 'Custom Error' instance using the provided property values + // and adds it to persistent data store and system cache. + // The method is smart enough to respect the transaction if used in an + // x.Atomic context. This method is asynchronous. + LogCustomError(source string, payload string) + // UpdateCustomError finds the 'Custom Error' instance using the provided unique identifier and updates it using + // the provided property values and reflects the changes to persistent data store and system + // cache. The method is smart enough to respect the transaction if used in an x.Atomic context. + // This method is synchronous. + UpdateCustomError(id int64) ICustomError + // UpdateCustomErrorObject finds and updates the 'Custom Error' using the provided instance and reflects the + // changes to persistent data store and system cache. The method is smart enough to + // respect the transaction if used in an x.Atomic context. This method is synchronous. + UpdateCustomErrorObject(object IObject, customError ICustomError) ICustomError + // AddOrUpdateCustomErrorObject tries to find the 'Custom Error' using the provided instance, then updates it in persistent + // data store and system cache or creates it if doesn't already exist. The method is smart enough + // to respect the transaction if used in an x.Atomic context. This method is synchronous. + AddOrUpdateCustomErrorObject(object IObject, customError ICustomError) ICustomError + // RemoveCustomError finds the 'Custom Error' instance using the provided unique identifier and + // removes it from the system cache. The method is smart enough to respect + // the transaction if used in an x.Atomic context. This method is synchronous. + RemoveCustomError(id int64) ICustomError + ResolveError(document IDocument) (IResolveErrorResult, error) + + // NewDocument creates a new 'Document' instance using the provided property values. + NewDocument(id int64, content string) (IDocument, error) + // NewDocuments creates an empty in-memory 'Document' collection which is not thread-safe. + NewDocuments() IDocumentCollection + // NewSystemSchedule creates a new 'System Schedule' instance using the provided property values. + NewSystemSchedule(id int64, enabled bool, config string) (ISystemSchedule, error) + // NewSystemSchedules creates an empty in-memory 'System Schedule' collection which is not thread-safe. + NewSystemSchedules() ISystemScheduleCollection + // NewIdentity creates a new 'Identity' instance using the provided property values. + NewIdentity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) (IIdentity, error) + // NewIdentities creates an empty in-memory 'Identity' collection which is not thread-safe. + NewIdentities() IIdentityCollection + // NewAccessControl creates a new 'Access Control' instance using the provided property values. + NewAccessControl(id int64, key uint64, value uint64) (IAccessControl, error) + // NewAccessControls creates an empty in-memory 'Access Control' collection which is not thread-safe. + NewAccessControls() IAccessControlCollection + // NewRemoteActivity creates a new 'Remote Activity' instance using the provided property values. + NewRemoteActivity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) (IRemoteActivity, error) + // NewRemoteActivities creates an empty in-memory 'Remote Activity' collection which is not thread-safe. + NewRemoteActivities() IRemoteActivityCollection + // NewCategoryType creates a new 'Category Type' instance using the provided property values. + NewCategoryType(id int64, description string) (ICategoryType, error) + // NewCategoryTypes creates an empty in-memory 'Category Type' collection which is not thread-safe. + NewCategoryTypes() ICategoryTypeCollection + // NewCategory creates a new 'Category' instance using the provided property values. + NewCategory(id int64, categoryTypeId int64, categoryId int64, title string, description string) (ICategory, error) + // NewCategories creates an empty in-memory 'Category' collection which is not thread-safe. + NewCategories() ICategoryCollection + // NewUser creates a new 'User' instance using the provided property values. + NewUser(id int64, github string) (IUser, error) + // NewUsers creates an empty in-memory 'User' collection which is not thread-safe. + NewUsers() IUserCollection + // NewSpi creates a new 'Spi' instance using the provided property values. + NewSpi() (ISpi, error) + // NewSpis creates an empty in-memory 'Spi' collection which is not thread-safe. + NewSpis() ISpiCollection + // NewCustomError creates a new 'Custom Error' instance using the provided property values. + NewCustomError() (ICustomError, error) + // NewCustomErrors creates an empty in-memory 'Custom Error' collection which is not thread-safe. + NewCustomErrors() ICustomErrorCollection + // NewEchoResult creates a new result container for 'Echo' system action. + NewEchoResult(document IDocument) IEchoResult + // NewResolveErrorResult creates a new result container for 'Resolve Error' system action. + NewResolveErrorResult() IResolveErrorResult + // Assert asserts the provided condition and panics if the assertion is not valid. + Assert(condition bool) IAssertionResult + // AssertNoError panics if the provided error is not nil. + AssertNoError(error) + // Ensure panics if any of the provided errors is not nil. + Ensure(...error) + // AssertNull panics if the provided interfaces is not nil. + AssertNull(interface{}) IAssertionResult + // AssertNotNull panics if the provided interfaces is nil. + AssertNotNull(interface{}) IAssertionResult + // AssertEmpty panic if the provided string is not empty. Trims the spaces in the string first. + AssertEmpty(input string) IAssertionResult + // AssertNotEmpty panic if the provided string is empty. Trims the spaces in the string first. + AssertNotEmpty(input string) IAssertionResult + // Format provides a wrapper around fmt.Sprintf + Format(format string, args ...interface{}) string + // Sort sorts the provided slice using the provided comparator function. + Sort(slice interface{}, less func(a, b int) bool) + // Search searches the input for any or all of the words in criteria. + Search(input, criteria string) bool + // Email sends an email message asynchronously. + Email(destination string, format string, args ...interface{}) + // SMS sends an sms message asynchronously. + SMS(destination string, format string, args ...interface{}) + // GenerateTrackingNumber returns a new random tracking number between 100000 and 999999. + GenerateTrackingNumber() uint32 + // GenerateUUID returns a new universal unique identifier. + GenerateUUID() string + // GenerateSalt returns a random salt string. + GenerateSalt() string + // GenerateHash returns a new hash from a string value and a salt. + GenerateHash(value string, salt string) string + // GenerateJwtToken returns a new jwt token. + GenerateJwtToken() string + // VerifyJwtToken verifies jwt token. + VerifyJwtToken(token string) error + // GenerateCode returns a random string code. + GenerateCode() string + // GenerateRSAKeyPair returns a new pair of public and private keys. + GenerateRSAKeyPair() (string, string, error) + // UnixNano returns the number of nanoseconds elapsed + // since January 1, 1970 UTC. The result is undefined if the Unix time + // in nanoseconds cannot be represented by an int64 (a date before the year + // 1678 or after 2262). The result does not depend on the location + UnixNano() int64 + // Trim trims the spaces in the input string + Trim(input string) string + // Contains reports whether substr is within s. + Contains(input, substr string) bool + // IsEmpty checks whether the provided string is empty. Trims the spaces in the string first. + IsEmpty(input string) bool + // IsNotEmpty checks whether the provided string is not empty. Trims the spaces in the string first. + IsNotEmpty(input string) bool + // IsSet returns true if the provided int64 is not zero. + IsSet(id int64) bool + // Join concatenates the elements of its first argument to create a single string. The separator + // string is placed between elements in the resulting string. + Join(elements []string, separator string) string + + IsTestEnvironment() bool + IsDevelopmentEnvironment() bool + IsStagingEnvironment() bool + IsProductionEnvironment() bool + + GetActivityStream(url string, data []byte, output interface{}) error + PostActivityStream(url string, data []byte, output interface{}) error + GetActivityStreamSigned(url, keyId, privateKey string, data []byte, output interface{}) error + PostActivityStreamSigned(url, keyId, privateKey string, data []byte, output interface{}) error +} diff --git a/greataped/components/contracts/system_dispatcher_cache.go b/greataped/components/contracts/system_dispatcher_cache.go new file mode 100644 index 0000000..5b275b0 --- /dev/null +++ b/greataped/components/contracts/system_dispatcher_cache.go @@ -0,0 +1,34 @@ +package contracts + +type IDispatcherCache interface { + + // Category + // ------------------------------------------------------------ + + // Returns a list of all 'Category' instances in the system + // that are children of the provided 'Category Type' instance. + ListCategoriesByCategoryType(categoryType ICategoryType) ICategoryCollection + // Returns a list of all 'Category' instances in the system that are + // children of the 'Category Type' instance with the provided unique identifier. + ListCategoriesByCategoryTypeId(categoryTypeId int64) ICategoryCollection + // Loops over all 'Category' instances in the system that are children + // of the provided 'Category Type' instance, running the provided iterator for each of them. + ForEachCategoryByCategoryType(categoryType ICategoryType, iterator CategoryIterator) + // Loops over all 'Category' instances in the system that are children + // of the 'Category Type' instance with the provided unique identifier, + // running the provided iterator for each of them. + ForEachCategoryByCategoryTypeId(categoryTypeId int64, iterator CategoryIterator) + // Returns a list of all 'Category' instances in the system + // that are children of the provided 'Category' instance. + ListCategoriesByCategory(category ICategory) ICategoryCollection + // Returns a list of all 'Category' instances in the system that are + // children of the 'Category' instance with the provided unique identifier. + ListCategoriesByCategoryId(categoryId int64) ICategoryCollection + // Loops over all 'Category' instances in the system that are children + // of the provided 'Category' instance, running the provided iterator for each of them. + ForEachCategoryByCategory(category ICategory, iterator CategoryIterator) + // Loops over all 'Category' instances in the system that are children + // of the 'Category' instance with the provided unique identifier, + // running the provided iterator for each of them. + ForEachCategoryByCategoryId(categoryId int64, iterator CategoryIterator) +} diff --git a/greataped/components/contracts/system_schedule.go b/greataped/components/contracts/system_schedule.go new file mode 100644 index 0000000..e6aa577 --- /dev/null +++ b/greataped/components/contracts/system_schedule.go @@ -0,0 +1,75 @@ +package contracts + +import . "github.com/xeronith/diamante/contracts/security" + +var SystemSchedulePassThroughFilter = func(ISystemSchedule) bool { return true } + +type ( + SystemSchedules []ISystemSchedule + SystemScheduleIterator func(ISystemSchedule) + SystemScheduleCondition func(ISystemSchedule) bool + SystemScheduleFilterPredicate func(ISystemSchedule) bool + SystemScheduleMapPredicate func(ISystemSchedule) ISystemSchedule + SystemScheduleCacheCallback func() + + ISystemSchedule interface { + IObject + // Enabled returns 'Enabled' of this 'SystemSchedule' instance. + Enabled() bool + // UpdateEnabled directly updates 'Enabled' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateEnabled(enabled bool, editor Identity) + // UpdateEnabledAtomic updates 'Enabled' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateEnabledAtomic(transaction ITransaction, enabled bool, editor Identity) + // Config returns 'Config' of this 'SystemSchedule' instance. + Config() string + // UpdateConfig directly updates 'Config' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateConfig(config string, editor Identity) + // UpdateConfigAtomic updates 'Config' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateConfigAtomic(transaction ITransaction, config string, editor Identity) + } + + ISystemScheduleCollection interface { + Count() int + IsEmpty() bool + IsNotEmpty() bool + HasExactlyOneItem() bool + HasAtLeastOneItem() bool + First() ISystemSchedule + Append(systemSchedule ISystemSchedule) + ForEach(SystemScheduleIterator) + Array() SystemSchedules + } + + ISystemScheduleManager interface { + ISystemComponent + OnCacheChanged(SystemScheduleCacheCallback) + Count() int + Exists(id int64) bool + ExistsWhich(condition SystemScheduleCondition) bool + ListSystemSchedules(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ISystemScheduleCollection + GetSystemSchedule(id int64, editor Identity) (ISystemSchedule, error) + AddSystemSchedule(enabled bool, config string, editor Identity) (ISystemSchedule, error) + AddSystemScheduleWithCustomId(id int64, enabled bool, config string, editor Identity) (ISystemSchedule, error) + AddSystemScheduleObject(systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) + AddSystemScheduleAtomic(transaction ITransaction, enabled bool, config string, editor Identity) (ISystemSchedule, error) + AddSystemScheduleWithCustomIdAtomic(id int64, transaction ITransaction, enabled bool, config string, editor Identity) (ISystemSchedule, error) + AddSystemScheduleObjectAtomic(transaction ITransaction, systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) + Log(enabled bool, config string, source string, editor Identity, payload string) + UpdateSystemSchedule(id int64, enabled bool, config string, editor Identity) (ISystemSchedule, error) + UpdateSystemScheduleObject(id int64, systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) + UpdateSystemScheduleAtomic(transaction ITransaction, id int64, enabled bool, config string, editor Identity) (ISystemSchedule, error) + UpdateSystemScheduleObjectAtomic(transaction ITransaction, id int64, systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) + AddOrUpdateSystemScheduleObject(id int64, systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) + AddOrUpdateSystemScheduleObjectAtomic(transaction ITransaction, id int64, systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) + RemoveSystemSchedule(id int64, editor Identity) (ISystemSchedule, error) + RemoveSystemScheduleAtomic(transaction ITransaction, id int64, editor Identity) (ISystemSchedule, error) + Find(id int64) ISystemSchedule + ForEach(iterator SystemScheduleIterator) + Filter(predicate SystemScheduleFilterPredicate) ISystemScheduleCollection + Map(predicate SystemScheduleMapPredicate) ISystemScheduleCollection + } +) diff --git a/greataped/components/contracts/user.go b/greataped/components/contracts/user.go new file mode 100644 index 0000000..ca5542f --- /dev/null +++ b/greataped/components/contracts/user.go @@ -0,0 +1,65 @@ +package contracts + +import . "github.com/xeronith/diamante/contracts/security" + +var UserPassThroughFilter = func(IUser) bool { return true } + +type ( + Users []IUser + UserIterator func(IUser) + UserCondition func(IUser) bool + UserFilterPredicate func(IUser) bool + UserMapPredicate func(IUser) IUser + UserCacheCallback func() + + IUser interface { + IObject + // Github returns 'Github' of this 'User' instance. + Github() string + // UpdateGithub directly updates 'Github' into persistent data store and + // refreshes the in-memory cache after successful update. + UpdateGithub(github string, editor Identity) + // UpdateGithubAtomic updates 'Github' into persistent data store through a transaction and + // refreshes the in-memory cache after successful commit. + UpdateGithubAtomic(transaction ITransaction, github string, editor Identity) + } + + IUserCollection interface { + Count() int + IsEmpty() bool + IsNotEmpty() bool + HasExactlyOneItem() bool + HasAtLeastOneItem() bool + First() IUser + Append(user IUser) + ForEach(UserIterator) + Array() Users + } + + IUserManager interface { + ISystemComponent + OnCacheChanged(UserCacheCallback) + Count() int + Exists(id int64) bool + ExistsWhich(condition UserCondition) bool + ListUsers(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IUserCollection + GetUser(id int64, editor Identity) (IUser, error) + AddUser(identityId int64, github string, editor Identity) (IUser, error) + AddUserObject(identityId int64, user IUser, editor Identity) (IUser, error) + AddUserAtomic(transaction ITransaction, identityId int64, github string, editor Identity) (IUser, error) + AddUserObjectAtomic(transaction ITransaction, identityId int64, user IUser, editor Identity) (IUser, error) + Log(identityId int64, github string, source string, editor Identity, payload string) + UpdateUser(id int64, github string, editor Identity) (IUser, error) + UpdateUserObject(id int64, user IUser, editor Identity) (IUser, error) + UpdateUserAtomic(transaction ITransaction, id int64, github string, editor Identity) (IUser, error) + UpdateUserObjectAtomic(transaction ITransaction, id int64, user IUser, editor Identity) (IUser, error) + AddOrUpdateUserObject(id int64, user IUser, editor Identity) (IUser, error) + AddOrUpdateUserObjectAtomic(transaction ITransaction, id int64, user IUser, editor Identity) (IUser, error) + RemoveUser(id int64, editor Identity) (IUser, error) + RemoveUserAtomic(transaction ITransaction, id int64, editor Identity) (IUser, error) + Find(id int64) IUser + ForEach(iterator UserIterator) + Filter(predicate UserFilterPredicate) IUserCollection + Map(predicate UserMapPredicate) IUserCollection + } +) diff --git a/greataped/components/core/access_control.go b/greataped/components/core/access_control.go new file mode 100644 index 0000000..878a692 --- /dev/null +++ b/greataped/components/core/access_control.go @@ -0,0 +1,306 @@ +package core + +import ( + "fmt" + + . "github.com/xeronith/diamante/contracts/security" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + "rail.town/infrastructure/components/model/repository" +) + +type accessControl struct { + object + key uint64 + value uint64 +} + +// noinspection GoUnusedExportedFunction +func InitializeAccessControl() { + _ = ENABLE_SECURITY + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + _ = repository.Initialize +} + +func NewAccessControl(id int64, key uint64, value uint64) (IAccessControl, error) { + instance := &accessControl{ + object: object{ + id: id, + }, + key: key, + value: value, + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func NewAccessControlFromEntity(entity IAccessControlEntity) (IAccessControl, error) { + instance := &accessControl{ + object: object{ + id: entity.Id(), + }, + key: entity.Key(), + value: entity.Value(), + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func (accessControl *accessControl) Key() uint64 { + return accessControl.key +} + +func (accessControl *accessControl) UpdateKey(key uint64, editor Identity) { + if err := repository.AccessControls.UpdateKey(accessControl.id, key, editor.Id()); err != nil { + panic(err.Error()) + } + + accessControl.key = key +} + +func (accessControl *accessControl) UpdateKeyAtomic(transaction ITransaction, key uint64, editor Identity) { + transaction.OnCommit(func() { + accessControl.key = key + }) + + if err := repository.AccessControls.UpdateKeyAtomic(transaction, accessControl.id, key, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (accessControl *accessControl) Value() uint64 { + return accessControl.value +} + +func (accessControl *accessControl) UpdateValue(value uint64, editor Identity) { + if err := repository.AccessControls.UpdateValue(accessControl.id, value, editor.Id()); err != nil { + panic(err.Error()) + } + + accessControl.value = value +} + +func (accessControl *accessControl) UpdateValueAtomic(transaction ITransaction, value uint64, editor Identity) { + transaction.OnCommit(func() { + accessControl.value = value + }) + + if err := repository.AccessControls.UpdateValueAtomic(transaction, accessControl.id, value, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (accessControl *accessControl) Validate() error { + return nil +} + +func (accessControl *accessControl) String() string { + return fmt.Sprintf("AccessControl (Id: %d, Key: %v, Value: %v)", accessControl.Id(), accessControl.Key(), accessControl.Value()) +} + +//------------------------------------------------------------------------------ + +type accessControls struct { + collection AccessControls +} + +// NewAccessControls creates an empty collection of 'Access Control' which is not thread-safe. +func NewAccessControls() IAccessControlCollection { + return &accessControls{ + collection: make(AccessControls, 0), + } +} + +func (accessControls *accessControls) Count() int { + return len(accessControls.collection) +} + +func (accessControls *accessControls) IsEmpty() bool { + return len(accessControls.collection) == 0 +} + +func (accessControls *accessControls) IsNotEmpty() bool { + return len(accessControls.collection) > 0 +} + +func (accessControls *accessControls) HasExactlyOneItem() bool { + return len(accessControls.collection) == 1 +} + +func (accessControls *accessControls) HasAtLeastOneItem() bool { + return len(accessControls.collection) >= 1 +} + +func (accessControls *accessControls) First() IAccessControl { + return accessControls.collection[0] +} + +func (accessControls *accessControls) Append(accessControl IAccessControl) { + accessControls.collection = append(accessControls.collection, accessControl) +} + +func (accessControls *accessControls) ForEach(iterator AccessControlIterator) { + if iterator == nil { + return + } + + for _, value := range accessControls.collection { + iterator(value) + } +} + +func (accessControls *accessControls) Array() AccessControls { + return accessControls.collection +} + +//------------------------------------------------------------------------------ + +func (dispatcher *dispatcher) AccessControlExists(id int64) bool { + return dispatcher.conductor.AccessControlManager().Exists(id) +} + +func (dispatcher *dispatcher) AccessControlExistsWhich(condition AccessControlCondition) bool { + return dispatcher.conductor.AccessControlManager().ExistsWhich(condition) +} + +func (dispatcher *dispatcher) ListAccessControls() IAccessControlCollection { + return dispatcher.conductor.AccessControlManager().ListAccessControls(0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachAccessControl(iterator AccessControlIterator) { + dispatcher.conductor.AccessControlManager().ForEach(iterator) +} + +func (dispatcher *dispatcher) FilterAccessControls(predicate AccessControlFilterPredicate) IAccessControlCollection { + return dispatcher.conductor.AccessControlManager().Filter(predicate) +} + +func (dispatcher *dispatcher) MapAccessControls(predicate AccessControlMapPredicate) IAccessControlCollection { + return dispatcher.conductor.AccessControlManager().Map(predicate) +} + +func (dispatcher *dispatcher) GetAccessControl(id int64) IAccessControl { + if accessControl, err := dispatcher.conductor.AccessControlManager().GetAccessControl(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } +} + +func (dispatcher *dispatcher) AddAccessControl(key uint64, value uint64) IAccessControl { + transaction := dispatcher.transaction + if transaction != nil { + if accessControl, err := dispatcher.conductor.AccessControlManager().AddAccessControlAtomic(transaction, key, value, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } else { + if accessControl, err := dispatcher.conductor.AccessControlManager().AddAccessControl(key, value, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } +} + +func (dispatcher *dispatcher) AddAccessControlWithCustomId(id int64, key uint64, value uint64) IAccessControl { + transaction := dispatcher.transaction + if transaction != nil { + if accessControl, err := dispatcher.conductor.AccessControlManager().AddAccessControlWithCustomIdAtomic(id, transaction, key, value, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } else { + if accessControl, err := dispatcher.conductor.AccessControlManager().AddAccessControlWithCustomId(id, key, value, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } +} + +func (dispatcher *dispatcher) LogAccessControl(key uint64, value uint64, source string, payload string) { + dispatcher.conductor.AccessControlManager().Log(key, value, source, dispatcher.identity, payload) +} + +func (dispatcher *dispatcher) UpdateAccessControl(id int64, key uint64, value uint64) IAccessControl { + transaction := dispatcher.transaction + if transaction != nil { + if accessControl, err := dispatcher.conductor.AccessControlManager().UpdateAccessControlAtomic(transaction, id, key, value, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } else { + if accessControl, err := dispatcher.conductor.AccessControlManager().UpdateAccessControl(id, key, value, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } +} + +// noinspection GoUnusedParameter +func (dispatcher *dispatcher) UpdateAccessControlObject(object IObject, accessControl IAccessControl) IAccessControl { + transaction := dispatcher.transaction + if transaction != nil { + if accessControl, err := dispatcher.conductor.AccessControlManager().UpdateAccessControlAtomic(transaction, object.Id(), accessControl.Key(), accessControl.Value(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } else { + if accessControl, err := dispatcher.conductor.AccessControlManager().UpdateAccessControl(object.Id(), accessControl.Key(), accessControl.Value(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } +} + +func (dispatcher *dispatcher) AddOrUpdateAccessControlObject(object IObject, accessControl IAccessControl) IAccessControl { + transaction := dispatcher.transaction + if transaction != nil { + if accessControl, err := dispatcher.conductor.AccessControlManager().AddOrUpdateAccessControlObjectAtomic(transaction, object.Id(), accessControl, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } else { + if accessControl, err := dispatcher.conductor.AccessControlManager().AddOrUpdateAccessControlObject(object.Id(), accessControl, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } +} + +func (dispatcher *dispatcher) RemoveAccessControl(id int64) IAccessControl { + transaction := dispatcher.transaction + if transaction != nil { + if accessControl, err := dispatcher.conductor.AccessControlManager().RemoveAccessControlAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } else { + if accessControl, err := dispatcher.conductor.AccessControlManager().RemoveAccessControl(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return accessControl + } + } +} diff --git a/greataped/components/core/access_control_manager.go b/greataped/components/core/access_control_manager.go new file mode 100644 index 0000000..eeccfb6 --- /dev/null +++ b/greataped/components/core/access_control_manager.go @@ -0,0 +1,293 @@ +package core + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/system" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" + "rail.town/infrastructure/components/model/repository" +) + +// noinspection GoSnakeCaseUsage +const ACCESS_CONTROL_MANAGER = "AccessControlManager" + +type accessControlManager struct { + systemComponent + cache ICache +} + +func newAccessControlManager(configuration IConfiguration, logger ILogger, dependencies ...ISystemComponent) IAccessControlManager { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + + manager := &accessControlManager{ + systemComponent: newSystemComponent(configuration, logger), + cache: NewCache(), + } + + if err := manager.ResolveDependencies(dependencies...); err != nil { + return nil + } + + return manager +} + +func (manager *accessControlManager) Name() string { + return ACCESS_CONTROL_MANAGER +} + +func (manager *accessControlManager) ResolveDependencies(_ ...ISystemComponent) error { + return nil +} + +func (manager *accessControlManager) Load() error { + accessControlEntities, err := repository.AccessControls.FetchAll() + if err != nil { + return err + } + + accessControls := make(SystemObjectCache) + for _, accessControlEntity := range accessControlEntities { + if accessControl, err := NewAccessControlFromEntity(accessControlEntity); err == nil { + accessControls[accessControl.Id()] = accessControl + } else { + return err + } + } + + manager.cache.Load(accessControls) + return nil +} + +func (manager *accessControlManager) Reload() error { + return manager.Load() +} + +func (manager *accessControlManager) OnCacheChanged(callback AccessControlCacheCallback) { + manager.cache.OnChanged(callback) +} + +func (manager *accessControlManager) Count() int { + return manager.cache.Size() +} + +func (manager *accessControlManager) Exists(id int64) bool { + return manager.Find(id) != nil +} + +func (manager *accessControlManager) ExistsWhich(condition AccessControlCondition) bool { + var accessControls AccessControls + manager.ForEach(func(accessControl IAccessControl) { + if condition(accessControl) { + accessControls = append(accessControls, accessControl) + } + }) + + return len(accessControls) > 0 +} + +func (manager *accessControlManager) ListAccessControls(_ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) IAccessControlCollection { + return manager.Filter(AccessControlPassThroughFilter) +} + +func (manager *accessControlManager) GetAccessControl(id int64, _ Identity) (IAccessControl, error) { + if accessControl := manager.Find(id); accessControl == nil { + return nil, ERROR_ACCESS_CONTROL_NOT_FOUND + } else { + return accessControl, nil + } +} + +func (manager *accessControlManager) AccessControls() map[uint64]uint64 { + result := make(map[uint64]uint64) + manager.ForEach(func(accessControl IAccessControl) { + result[accessControl.Key()] = accessControl.Value() + }) + + return result +} + +func (manager *accessControlManager) AddOrUpdateAccessControl(key uint64, value uint64, editor Identity) error { + var accessControl IAccessControl + for _, _accessControl := range manager.Filter(AccessControlPassThroughFilter).Array() { + if _accessControl.Key() == key { + accessControl = _accessControl + break + } + } + + if accessControl != nil { + _, err := manager.UpdateAccessControl(accessControl.Id(), accessControl.Key(), value, editor) + return err + } else { + _, err := manager.AddAccessControl(key, value, editor) + return err + } +} + +func (manager *accessControlManager) AddAccessControl(key uint64, value uint64, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(manager.UniqueId(), key, value) + return manager.Apply(accessControlEntity, repository.AccessControls.Add, manager.cache.Put, editor) +} + +func (manager *accessControlManager) AddAccessControlWithCustomId(id int64, key uint64, value uint64, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(id, key, value) + return manager.Apply(accessControlEntity, repository.AccessControls.Add, manager.cache.Put, editor) +} + +func (manager *accessControlManager) AddAccessControlObject(accessControl IAccessControl, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(manager.UniqueId(), accessControl.Key(), accessControl.Value()) + return manager.Apply(accessControlEntity, repository.AccessControls.Add, manager.cache.Put, editor) +} + +func (manager *accessControlManager) AddAccessControlAtomic(transaction ITransaction, key uint64, value uint64, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(manager.UniqueId(), key, value) + return manager.ApplyAtomic(transaction, accessControlEntity, repository.AccessControls.AddAtomic, manager.cache.Put, editor) +} + +func (manager *accessControlManager) AddAccessControlWithCustomIdAtomic(id int64, transaction ITransaction, key uint64, value uint64, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(id, key, value) + return manager.ApplyAtomic(transaction, accessControlEntity, repository.AccessControls.AddAtomic, manager.cache.Put, editor) +} + +func (manager *accessControlManager) AddAccessControlObjectAtomic(transaction ITransaction, accessControl IAccessControl, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(manager.UniqueId(), accessControl.Key(), accessControl.Value()) + return manager.ApplyAtomic(transaction, accessControlEntity, repository.AccessControls.AddAtomic, manager.cache.Put, editor) +} + +func (manager *accessControlManager) Log(key uint64, value uint64, source string, editor Identity, payload string) { + accessControlPipeEntity := NewAccessControlPipeEntity(manager.UniqueId(), key, value, source, editor.Id(), payload) + repository.Pipe.Insert(accessControlPipeEntity) + + accessControl, err := NewAccessControlFromEntity(accessControlPipeEntity) + if err != nil { + manager.Logger().Error(err) + } else { + manager.cache.Put(accessControl.Id(), accessControl) + } +} + +func (manager *accessControlManager) UpdateAccessControl(id int64, key uint64, value uint64, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(id, key, value) + return manager.Apply(accessControlEntity, repository.AccessControls.Update, manager.cache.Put, editor) +} + +func (manager *accessControlManager) UpdateAccessControlObject(id int64, accessControl IAccessControl, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(id, accessControl.Key(), accessControl.Value()) + return manager.Apply(accessControlEntity, repository.AccessControls.Update, manager.cache.Put, editor) +} + +func (manager *accessControlManager) UpdateAccessControlAtomic(transaction ITransaction, id int64, key uint64, value uint64, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(id, key, value) + return manager.ApplyAtomic(transaction, accessControlEntity, repository.AccessControls.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *accessControlManager) UpdateAccessControlObjectAtomic(transaction ITransaction, id int64, accessControl IAccessControl, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(id, accessControl.Key(), accessControl.Value()) + return manager.ApplyAtomic(transaction, accessControlEntity, repository.AccessControls.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *accessControlManager) AddOrUpdateAccessControlObject(id int64, accessControl IAccessControl, editor Identity) (IAccessControl, error) { + if manager.Exists(id) { + return manager.UpdateAccessControlObject(id, accessControl, editor) + } else { + return manager.AddAccessControlObject(accessControl, editor) + } +} + +func (manager *accessControlManager) AddOrUpdateAccessControlObjectAtomic(transaction ITransaction, id int64, accessControl IAccessControl, editor Identity) (IAccessControl, error) { + if manager.Exists(id) { + return manager.UpdateAccessControlObjectAtomic(transaction, id, accessControl, editor) + } else { + return manager.AddAccessControlObjectAtomic(transaction, accessControl, editor) + } +} + +func (manager *accessControlManager) RemoveAccessControl(id int64, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(id, 0, 0) + return manager.Apply(accessControlEntity, repository.AccessControls.Remove, manager.cache.Remove, editor) +} + +func (manager *accessControlManager) RemoveAccessControlAtomic(transaction ITransaction, id int64, editor Identity) (IAccessControl, error) { + accessControlEntity := NewAccessControlEntity(id, 0, 0) + return manager.ApplyAtomic(transaction, accessControlEntity, repository.AccessControls.RemoveAtomic, manager.cache.Remove, editor) +} + +func (manager *accessControlManager) Apply(accessControlEntity IAccessControlEntity, repositoryHandler func(IAccessControlEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (IAccessControl, error) { + result, err := NewAccessControlFromEntity(accessControlEntity) + if err != nil { + return nil, err + } + + if err := repositoryHandler(accessControlEntity, editor.Id()); err != nil { + return nil, err + } + + cacheHandler(result.Id(), result) + return result, nil +} + +func (manager *accessControlManager) ApplyAtomic(transaction ITransaction, accessControlEntity IAccessControlEntity, repositoryHandler func(IRepositoryTransaction, IAccessControlEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (IAccessControl, error) { + result, err := NewAccessControlFromEntity(accessControlEntity) + if err != nil { + return nil, err + } + + transaction.OnCommit(func() { + cacheHandler(result.Id(), result) + }) + + if err := repositoryHandler(transaction, accessControlEntity, editor.Id()); err != nil { + return nil, err + } + + return result, nil +} + +func (manager *accessControlManager) Find(id int64) IAccessControl { + if object, exists := manager.cache.Get(id); !exists { + return nil + } else { + return object.(IAccessControl) + } +} + +func (manager *accessControlManager) ForEach(iterator AccessControlIterator) { + manager.cache.ForEachValue(func(object ISystemObject) { + iterator(object.(IAccessControl)) + }) +} + +func (manager *accessControlManager) Filter(predicate AccessControlFilterPredicate) IAccessControlCollection { + accessControls := NewAccessControls() + if predicate == nil { + return accessControls + } + + manager.ForEach(func(accessControl IAccessControl) { + if predicate(accessControl) { + accessControls.Append(accessControl) + } + }) + + return accessControls +} + +func (manager *accessControlManager) Map(predicate AccessControlMapPredicate) IAccessControlCollection { + accessControls := NewAccessControls() + if predicate == nil { + return accessControls + } + + manager.ForEach(func(accessControl IAccessControl) { + accessControls.Append(predicate(accessControl)) + }) + + return accessControls +} diff --git a/greataped/components/core/access_control_manager_test.go b/greataped/components/core/access_control_manager_test.go new file mode 100644 index 0000000..dcb69d7 --- /dev/null +++ b/greataped/components/core/access_control_manager_test.go @@ -0,0 +1,150 @@ +package core_test + +import ( + "testing" + + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/core" +) + +func TestAccessControlManager_GetName(test *testing.T) { + manager := Conductor.AccessControlManager() + + if manager.Name() != ACCESS_CONTROL_MANAGER { + test.Fail() + } +} + +func TestAccessControlManager_ResolveDependencies(test *testing.T) { + manager := Conductor.AccessControlManager() + + if err := manager.ResolveDependencies(); err != nil { + test.Fatal(err) + } +} + +func TestAccessControlManager_Load(test *testing.T) { + manager := Conductor.AccessControlManager() + + if err := manager.Load(); err != nil { + test.Fatal(err) + } +} + +func TestAccessControlManager_Reload(test *testing.T) { + manager := Conductor.AccessControlManager() + + if err := manager.Reload(); err != nil && err != ERROR_OPERATION_NOT_SUPPORTED { + test.Fatal(err) + } +} + +func TestAccessControlManager_Count(test *testing.T) { + manager := Conductor.AccessControlManager() + + _ = manager.Count() +} + +func TestAccessControlManager_Exists(test *testing.T) { + manager := Conductor.AccessControlManager() + + if manager.Exists(0) { + test.FailNow() + } +} + +func TestAccessControlManager_ListAccessControls(test *testing.T) { + manager := Conductor.AccessControlManager() + + _ = manager.ListAccessControls(0, 0, "", nil) +} + +func TestAccessControlManager_GetAccessControl(test *testing.T) { + manager := Conductor.AccessControlManager() + + if accessControl, err := manager.GetAccessControl(0, nil); err == nil { + _ = accessControl + test.FailNow() + } +} + +func TestAccessControlManager_AddAccessControl(test *testing.T) { + manager := Conductor.AccessControlManager() + + accessControl, err := manager.AddAccessControl(0, 0, nil) + if err != nil { + test.Fatal(err) + } + + _ = accessControl +} + +func TestAccessControlManager_UpdateAccessControl(test *testing.T) { + manager := Conductor.AccessControlManager() + + accessControl, err := manager.UpdateAccessControl(0, 0, 0, nil) + if err != nil { + test.Fatal(err) + } + + _ = accessControl +} + +func TestAccessControlManager_RemoveAccessControl(test *testing.T) { + manager := Conductor.AccessControlManager() + + accessControl, err := manager.RemoveAccessControl(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = accessControl +} + +func TestAccessControlManager_Find(test *testing.T) { + manager := Conductor.AccessControlManager() + + accessControl := manager.Find(0) + if accessControl == nil { + test.Fail() + } + + _ = accessControl +} + +func TestAccessControlManager_ForEach(test *testing.T) { + manager := Conductor.AccessControlManager() + + manager.ForEach(func(accessControl IAccessControl) { + _ = accessControl + }) +} + +func TestAccessControlManager_Filter(test *testing.T) { + manager := Conductor.AccessControlManager() + + accessControls := manager.Filter(func(accessControl IAccessControl) bool { + return accessControl.Id() < 0 + }) + + if accessControls.IsNotEmpty() { + test.Fail() + } + + _ = accessControls +} + +func TestAccessControlManager_Map(test *testing.T) { + manager := Conductor.AccessControlManager() + + accessControls := manager.Map(func(accessControl IAccessControl) IAccessControl { + return accessControl + }) + + if accessControls.Count() != manager.Count() { + test.Fail() + } + + _ = accessControls +} diff --git a/greataped/components/core/api_client.go b/greataped/components/core/api_client.go new file mode 100644 index 0000000..e8226e3 --- /dev/null +++ b/greataped/components/core/api_client.go @@ -0,0 +1,113 @@ +package core + +import ( + "errors" + "fmt" + "reflect" + "time" + + . "github.com/xeronith/diamante/client" + . "github.com/xeronith/diamante/contracts/client" + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/operation" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/server" + . "rail.town/infrastructure/components/api/protobuf" + . "rail.town/infrastructure/components/contracts" +) + +// noinspection GoSnakeCaseUsage +var API_RESULT = make(map[uint64]Pointer) + +//-------------------------------------------------------------------------------------- + +type api struct { + client IClient + output chan *output + logger ILogger + debugMode bool +} + +func NewApi(endpoint string, logger ILogger) IApi { + initialized := make(chan bool, 1) + + httpClient := NewHttpClient() + httpClient.SetName("API-CLIENT") + httpClient.SetVersion(1) + httpClient.SetApiVersion(1) + httpClient.OnConnectionEstablished(func(client IClient) { + initialized <- true + }) + + _ = httpClient.Connect(endpoint, "AUTH-TOKEN") + + <-initialized + api := &api{ + client: httpClient, + output: make(chan *output, 1), + logger: logger, + } + + api.client.SetBinaryOperationResultListener(api.handler) + return api +} + +func (api *api) SetToken(token string) { + api.client.SetToken(token) +} + +func (api *api) SetDebugMode(enabled bool) { + api.debugMode = enabled +} + +func (api *api) call(operation uint64, payload Pointer) (Pointer, error) { + requestId := uint64(time.Now().UnixNano()) + if api.debugMode { + api.logger.Debug(fmt.Sprintf("REQ { ID: %d, OP: %s }", requestId, OPCODES[operation])) + } + + if err := api.client.Send(requestId, operation, payload); err != nil { + return nil, err + } + + result := <-api.output + if result.isError { + return nil, errors.New(result.payload.(*Error).Message) + } + + return result.payload, nil +} + +func (api *api) handler(bundle IBinaryOperationResult) { + isError := false + var result Pointer + + _type, exists := API_RESULT[bundle.Type()] + if exists { + resultType := reflect.TypeOf(_type) + result = reflect.New(resultType).Interface() + } else { + switch bundle.Type() { + case ERROR: + result = new(Error) + isError = true + default: + api.logger.Fatal("unregistered_result_type") + } + } + + if err := DefaultBinarySerializer.Deserialize(bundle.Payload(), result); err != nil { + api.logger.Fatal(err) + } + + api.output <- &output{payload: result, isError: isError} +} + +//-------------------------------------------------------------------------------------- + +type output struct { + payload Pointer + isError bool +} + +//-------------------------------------------------------------------------------------- diff --git a/greataped/components/core/api_methods.go b/greataped/components/core/api_methods.go new file mode 100644 index 0000000..0ef3a1a --- /dev/null +++ b/greataped/components/core/api_methods.go @@ -0,0 +1,42 @@ +package core + +import ( + . "rail.town/infrastructure/components/api/protobuf" + . "rail.town/infrastructure/components/contracts" +) + +func (api *api) SystemCall(request *SystemCallRequest) (*SystemCallResult, error) { + result, err := api.call(SYSTEM_CALL_REQUEST, request) + + if err != nil { + return nil, err + } else { + return result.(*SystemCallResult), nil + } +} + +func (api *api) Echo(request *EchoRequest) (*EchoResult, error) { + result, err := api.call(ECHO_REQUEST, request) + + if err != nil { + return nil, err + } else { + return result.(*EchoResult), nil + } +} + +func (api *api) ResolveError(request *ResolveErrorRequest) (*ResolveErrorResult, error) { + result, err := api.call(RESOLVE_ERROR_REQUEST, request) + + if err != nil { + return nil, err + } else { + return result.(*ResolveErrorResult), nil + } +} + +func init() { + API_RESULT[SYSTEM_CALL_RESULT] = SystemCallResult{} + API_RESULT[ECHO_RESULT] = EchoResult{} + API_RESULT[RESOLVE_ERROR_RESULT] = ResolveErrorResult{} +} diff --git a/greataped/components/core/category.go b/greataped/components/core/category.go new file mode 100644 index 0000000..cc5fdd4 --- /dev/null +++ b/greataped/components/core/category.go @@ -0,0 +1,401 @@ +package core + +import ( + "fmt" + + . "github.com/xeronith/diamante/contracts/security" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + "rail.town/infrastructure/components/model/repository" +) + +type category struct { + object + categoryTypeId int64 + categoryId int64 + title string + description string +} + +// noinspection GoUnusedExportedFunction +func InitializeCategory() { + _ = ENABLE_SECURITY + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + _ = repository.Initialize +} + +func NewCategory(id int64, categoryTypeId int64, categoryId int64, title string, description string) (ICategory, error) { + instance := &category{ + object: object{ + id: id, + }, + categoryTypeId: categoryTypeId, + categoryId: categoryId, + title: title, + description: description, + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func NewCategoryFromEntity(entity ICategoryEntity) (ICategory, error) { + instance := &category{ + object: object{ + id: entity.Id(), + }, + categoryTypeId: entity.CategoryTypeId(), + categoryId: entity.CategoryId(), + title: entity.Title(), + description: entity.Description(), + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func (category *category) DependenciesAreUnknown() bool { + // noinspection GoBoolExpressions + return category.categoryTypeId == 0 || category.categoryId == 0 || false +} + +func (category *category) CategoryTypeId() int64 { + return category.categoryTypeId +} + +func (category *category) AssertBelongsToCategoryType(_categoryType ICategoryType) { + if category.categoryTypeId != _categoryType.Id() { + panic(ERROR_MESSAGE_CATEGORY_NOT_FOUND) + } +} + +func (category *category) CategoryTypeIsUnknown() bool { + return category.categoryTypeId == 0 +} + +func (category *category) AssertCategoryTypeIsProvided() { + if category.categoryTypeId == 0 { + panic(ERROR_MESSAGE_UNKNOWN_CATEGORY_TYPE) + } +} + +func (category *category) AssertCategoryType(categoryTypeId int64) { + if category.categoryTypeId != categoryTypeId { + panic(ERROR_MESSAGE_UNKNOWN_CATEGORY_TYPE) + } +} + +func (category *category) CategoryId() int64 { + return category.categoryId +} + +func (category *category) AssertBelongsToCategory(_category ICategory) { + if category.categoryId != _category.Id() { + panic(ERROR_MESSAGE_CATEGORY_NOT_FOUND) + } +} + +func (category *category) CategoryIsUnknown() bool { + return category.categoryId == 0 +} + +func (category *category) AssertCategoryIsProvided() { + if category.categoryId == 0 { + panic(ERROR_MESSAGE_UNKNOWN_CATEGORY) + } +} + +func (category *category) AssertCategory(categoryId int64) { + if category.categoryId != categoryId { + panic(ERROR_MESSAGE_UNKNOWN_CATEGORY) + } +} + +func (category *category) Title() string { + return category.title +} + +func (category *category) UpdateTitle(title string, editor Identity) { + if err := repository.Categories.UpdateTitle(category.id, title, editor.Id()); err != nil { + panic(err.Error()) + } + + category.title = title +} + +func (category *category) UpdateTitleAtomic(transaction ITransaction, title string, editor Identity) { + transaction.OnCommit(func() { + category.title = title + }) + + if err := repository.Categories.UpdateTitleAtomic(transaction, category.id, title, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (category *category) Description() string { + return category.description +} + +func (category *category) UpdateDescription(description string, editor Identity) { + if err := repository.Categories.UpdateDescription(category.id, description, editor.Id()); err != nil { + panic(err.Error()) + } + + category.description = description +} + +func (category *category) UpdateDescriptionAtomic(transaction ITransaction, description string, editor Identity) { + transaction.OnCommit(func() { + category.description = description + }) + + if err := repository.Categories.UpdateDescriptionAtomic(transaction, category.id, description, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (category *category) Validate() error { + return nil +} + +func (category *category) String() string { + return fmt.Sprintf("Category (Id: %d, CategoryTypeId: %d, CategoryId: %d, Title: %v, Description: %v)", category.Id(), category.CategoryTypeId(), category.CategoryId(), category.Title(), category.Description()) +} + +//------------------------------------------------------------------------------ + +type categories struct { + collection Categories +} + +// NewCategories creates an empty collection of 'Category' which is not thread-safe. +func NewCategories() ICategoryCollection { + return &categories{ + collection: make(Categories, 0), + } +} + +func (categories *categories) Count() int { + return len(categories.collection) +} + +func (categories *categories) IsEmpty() bool { + return len(categories.collection) == 0 +} + +func (categories *categories) IsNotEmpty() bool { + return len(categories.collection) > 0 +} + +func (categories *categories) HasExactlyOneItem() bool { + return len(categories.collection) == 1 +} + +func (categories *categories) HasAtLeastOneItem() bool { + return len(categories.collection) >= 1 +} + +func (categories *categories) First() ICategory { + return categories.collection[0] +} + +func (categories *categories) Append(category ICategory) { + categories.collection = append(categories.collection, category) +} + +func (categories *categories) ForEach(iterator CategoryIterator) { + if iterator == nil { + return + } + + for _, value := range categories.collection { + iterator(value) + } +} + +func (categories *categories) Array() Categories { + return categories.collection +} + +//------------------------------------------------------------------------------ + +func (dispatcher *dispatcher) CategoryExists(id int64) bool { + return dispatcher.conductor.CategoryManager().Exists(id) +} + +func (dispatcher *dispatcher) CategoryExistsWhich(condition CategoryCondition) bool { + return dispatcher.conductor.CategoryManager().ExistsWhich(condition) +} + +func (dispatcher *dispatcher) ListCategories() ICategoryCollection { + return dispatcher.conductor.CategoryManager().ListCategories(0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachCategory(iterator CategoryIterator) { + dispatcher.conductor.CategoryManager().ForEach(iterator) +} + +func (dispatcher *dispatcher) FilterCategories(predicate CategoryFilterPredicate) ICategoryCollection { + return dispatcher.conductor.CategoryManager().Filter(predicate) +} + +func (dispatcher *dispatcher) MapCategories(predicate CategoryMapPredicate) ICategoryCollection { + return dispatcher.conductor.CategoryManager().Map(predicate) +} + +func (dispatcher *dispatcher) GetCategory(id int64) ICategory { + if category, err := dispatcher.conductor.CategoryManager().GetCategory(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } +} + +func (dispatcher *dispatcher) AddCategory(categoryTypeId int64, categoryId int64, title string, description string) ICategory { + transaction := dispatcher.transaction + if transaction != nil { + if category, err := dispatcher.conductor.CategoryManager().AddCategoryAtomic(transaction, categoryTypeId, categoryId, title, description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } else { + if category, err := dispatcher.conductor.CategoryManager().AddCategory(categoryTypeId, categoryId, title, description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } +} + +func (dispatcher *dispatcher) AddCategoryWithCustomId(id int64, categoryTypeId int64, categoryId int64, title string, description string) ICategory { + transaction := dispatcher.transaction + if transaction != nil { + if category, err := dispatcher.conductor.CategoryManager().AddCategoryWithCustomIdAtomic(id, transaction, categoryTypeId, categoryId, title, description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } else { + if category, err := dispatcher.conductor.CategoryManager().AddCategoryWithCustomId(id, categoryTypeId, categoryId, title, description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } +} + +func (dispatcher *dispatcher) LogCategory(categoryTypeId int64, categoryId int64, title string, description string, source string, payload string) { + dispatcher.conductor.CategoryManager().Log(categoryTypeId, categoryId, title, description, source, dispatcher.identity, payload) +} + +func (dispatcher *dispatcher) UpdateCategory(id int64, categoryTypeId int64, categoryId int64, title string, description string) ICategory { + transaction := dispatcher.transaction + if transaction != nil { + if category, err := dispatcher.conductor.CategoryManager().UpdateCategoryAtomic(transaction, id, categoryTypeId, categoryId, title, description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } else { + if category, err := dispatcher.conductor.CategoryManager().UpdateCategory(id, categoryTypeId, categoryId, title, description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } +} + +// noinspection GoUnusedParameter +func (dispatcher *dispatcher) UpdateCategoryObject(object IObject, category ICategory) ICategory { + transaction := dispatcher.transaction + if transaction != nil { + if category, err := dispatcher.conductor.CategoryManager().UpdateCategoryAtomic(transaction, object.Id(), category.CategoryTypeId(), category.CategoryId(), category.Title(), category.Description(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } else { + if category, err := dispatcher.conductor.CategoryManager().UpdateCategory(object.Id(), category.CategoryTypeId(), category.CategoryId(), category.Title(), category.Description(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } +} + +func (dispatcher *dispatcher) AddOrUpdateCategoryObject(object IObject, category ICategory) ICategory { + transaction := dispatcher.transaction + if transaction != nil { + if category, err := dispatcher.conductor.CategoryManager().AddOrUpdateCategoryObjectAtomic(transaction, object.Id(), category, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } else { + if category, err := dispatcher.conductor.CategoryManager().AddOrUpdateCategoryObject(object.Id(), category, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } +} + +func (dispatcher *dispatcher) RemoveCategory(id int64) ICategory { + transaction := dispatcher.transaction + if transaction != nil { + if category, err := dispatcher.conductor.CategoryManager().RemoveCategoryAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } else { + if category, err := dispatcher.conductor.CategoryManager().RemoveCategory(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return category + } + } +} + +func (dispatcher *dispatcher) ListCategoriesByCategoryType(categoryType ICategoryType) ICategoryCollection { + return dispatcher.conductor.CategoryManager().ListCategoriesByCategoryType(categoryType.Id(), 0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ListCategoriesByCategoryTypeId(categoryTypeId int64) ICategoryCollection { + return dispatcher.conductor.CategoryManager().ListCategoriesByCategoryType(categoryTypeId, 0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachCategoryByCategoryType(categoryType ICategoryType, iterator CategoryIterator) { + dispatcher.conductor.CategoryManager().ForEachByCategoryType(categoryType.Id(), iterator) +} + +func (dispatcher *dispatcher) ForEachCategoryByCategoryTypeId(categoryTypeId int64, iterator CategoryIterator) { + dispatcher.conductor.CategoryManager().ForEachByCategoryType(categoryTypeId, iterator) +} + +func (dispatcher *dispatcher) ListCategoriesByCategory(category ICategory) ICategoryCollection { + return dispatcher.conductor.CategoryManager().ListCategoriesByCategory(category.Id(), 0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ListCategoriesByCategoryId(categoryId int64) ICategoryCollection { + return dispatcher.conductor.CategoryManager().ListCategoriesByCategory(categoryId, 0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachCategoryByCategory(category ICategory, iterator CategoryIterator) { + dispatcher.conductor.CategoryManager().ForEachByCategory(category.Id(), iterator) +} + +func (dispatcher *dispatcher) ForEachCategoryByCategoryId(categoryId int64, iterator CategoryIterator) { + dispatcher.conductor.CategoryManager().ForEachByCategory(categoryId, iterator) +} diff --git a/greataped/components/core/category_manager.go b/greataped/components/core/category_manager.go new file mode 100644 index 0000000..8c618eb --- /dev/null +++ b/greataped/components/core/category_manager.go @@ -0,0 +1,322 @@ +package core + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/system" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" + "rail.town/infrastructure/components/model/repository" +) + +// noinspection GoSnakeCaseUsage +const CATEGORY_MANAGER = "CategoryManager" + +//lint:ignore U1000 GoUnused +type categoryManager struct { + systemComponent + cache ICache + + //Dependencies + categoryTypeManager ICategoryTypeManager + categoryManager ICategoryManager +} + +func newCategoryManager(configuration IConfiguration, logger ILogger, dependencies ...ISystemComponent) ICategoryManager { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + + manager := &categoryManager{ + systemComponent: newSystemComponent(configuration, logger), + cache: NewCache(), + } + + if err := manager.ResolveDependencies(dependencies...); err != nil { + return nil + } + + return manager +} + +func (manager *categoryManager) Name() string { + return CATEGORY_MANAGER +} + +func (manager *categoryManager) ResolveDependencies(dependencies ...ISystemComponent) error { + if len(dependencies) == 0 { + return nil + } + + var ( + categoryTypeManager ICategoryTypeManager + categoryManager ICategoryManager + ) + + for _, _dependency := range dependencies { + if false { + } else if dependency, ok := _dependency.(ICategoryTypeManager); ok { + categoryTypeManager = dependency + } else if dependency, ok := _dependency.(ICategoryManager); ok { + categoryManager = dependency + } + } + + if // noinspection GoBoolExpressions + false || categoryTypeManager == nil || categoryManager == nil { + return ERROR_UNRESOLVED_DEPENDENCIES + } + + return nil +} + +func (manager *categoryManager) Load() error { + categoryEntities, err := repository.Categories.FetchAll() + if err != nil { + return err + } + + categories := make(SystemObjectCache) + for _, categoryEntity := range categoryEntities { + if category, err := NewCategoryFromEntity(categoryEntity); err == nil { + categories[category.Id()] = category + } else { + return err + } + } + + manager.cache.Load(categories) + return nil +} + +func (manager *categoryManager) Reload() error { + return manager.Load() +} + +func (manager *categoryManager) OnCacheChanged(callback CategoryCacheCallback) { + manager.cache.OnChanged(callback) +} + +func (manager *categoryManager) Count() int { + return manager.cache.Size() +} + +func (manager *categoryManager) Exists(id int64) bool { + return manager.Find(id) != nil +} + +func (manager *categoryManager) ExistsWhich(condition CategoryCondition) bool { + var categories Categories + manager.ForEach(func(category ICategory) { + if condition(category) { + categories = append(categories, category) + } + }) + + return len(categories) > 0 +} + +func (manager *categoryManager) ListCategories(_ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) ICategoryCollection { + return manager.Filter(CategoryPassThroughFilter) +} + +func (manager *categoryManager) GetCategory(id int64, _ Identity) (ICategory, error) { + if category := manager.Find(id); category == nil { + return nil, ERROR_CATEGORY_NOT_FOUND + } else { + return category, nil + } +} + +func (manager *categoryManager) AddCategory(categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(manager.UniqueId(), categoryTypeId, categoryId, title, description) + return manager.Apply(categoryEntity, repository.Categories.Add, manager.cache.Put, editor) +} + +func (manager *categoryManager) AddCategoryWithCustomId(id int64, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(id, categoryTypeId, categoryId, title, description) + return manager.Apply(categoryEntity, repository.Categories.Add, manager.cache.Put, editor) +} + +func (manager *categoryManager) AddCategoryObject(category ICategory, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(manager.UniqueId(), category.CategoryTypeId(), category.CategoryId(), category.Title(), category.Description()) + return manager.Apply(categoryEntity, repository.Categories.Add, manager.cache.Put, editor) +} + +func (manager *categoryManager) AddCategoryAtomic(transaction ITransaction, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(manager.UniqueId(), categoryTypeId, categoryId, title, description) + return manager.ApplyAtomic(transaction, categoryEntity, repository.Categories.AddAtomic, manager.cache.Put, editor) +} + +func (manager *categoryManager) AddCategoryWithCustomIdAtomic(id int64, transaction ITransaction, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(id, categoryTypeId, categoryId, title, description) + return manager.ApplyAtomic(transaction, categoryEntity, repository.Categories.AddAtomic, manager.cache.Put, editor) +} + +func (manager *categoryManager) AddCategoryObjectAtomic(transaction ITransaction, category ICategory, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(manager.UniqueId(), category.CategoryTypeId(), category.CategoryId(), category.Title(), category.Description()) + return manager.ApplyAtomic(transaction, categoryEntity, repository.Categories.AddAtomic, manager.cache.Put, editor) +} + +func (manager *categoryManager) Log(categoryTypeId int64, categoryId int64, title string, description string, source string, editor Identity, payload string) { + categoryPipeEntity := NewCategoryPipeEntity(manager.UniqueId(), categoryTypeId, categoryId, title, description, source, editor.Id(), payload) + repository.Pipe.Insert(categoryPipeEntity) + + category, err := NewCategoryFromEntity(categoryPipeEntity) + if err != nil { + manager.Logger().Error(err) + } else { + manager.cache.Put(category.Id(), category) + } +} + +func (manager *categoryManager) UpdateCategory(id int64, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(id, categoryTypeId, categoryId, title, description) + return manager.Apply(categoryEntity, repository.Categories.Update, manager.cache.Put, editor) +} + +func (manager *categoryManager) UpdateCategoryObject(id int64, category ICategory, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(id, category.Id(), category.Id(), category.Title(), category.Description()) + return manager.Apply(categoryEntity, repository.Categories.Update, manager.cache.Put, editor) +} + +func (manager *categoryManager) UpdateCategoryAtomic(transaction ITransaction, id int64, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(id, categoryTypeId, categoryId, title, description) + return manager.ApplyAtomic(transaction, categoryEntity, repository.Categories.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *categoryManager) UpdateCategoryObjectAtomic(transaction ITransaction, id int64, category ICategory, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(id, category.Id(), category.Id(), category.Title(), category.Description()) + return manager.ApplyAtomic(transaction, categoryEntity, repository.Categories.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *categoryManager) AddOrUpdateCategoryObject(id int64, category ICategory, editor Identity) (ICategory, error) { + if manager.Exists(id) { + return manager.UpdateCategoryObject(id, category, editor) + } else { + return manager.AddCategoryObject(category, editor) + } +} + +func (manager *categoryManager) AddOrUpdateCategoryObjectAtomic(transaction ITransaction, id int64, category ICategory, editor Identity) (ICategory, error) { + if manager.Exists(id) { + return manager.UpdateCategoryObjectAtomic(transaction, id, category, editor) + } else { + return manager.AddCategoryObjectAtomic(transaction, category, editor) + } +} + +func (manager *categoryManager) RemoveCategory(id int64, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(id, 0, 0, "", "") + return manager.Apply(categoryEntity, repository.Categories.Remove, manager.cache.Remove, editor) +} + +func (manager *categoryManager) RemoveCategoryAtomic(transaction ITransaction, id int64, editor Identity) (ICategory, error) { + categoryEntity := NewCategoryEntity(id, 0, 0, "", "") + return manager.ApplyAtomic(transaction, categoryEntity, repository.Categories.RemoveAtomic, manager.cache.Remove, editor) +} + +func (manager *categoryManager) Apply(categoryEntity ICategoryEntity, repositoryHandler func(ICategoryEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (ICategory, error) { + result, err := NewCategoryFromEntity(categoryEntity) + if err != nil { + return nil, err + } + + if err := repositoryHandler(categoryEntity, editor.Id()); err != nil { + return nil, err + } + + cacheHandler(result.Id(), result) + return result, nil +} + +func (manager *categoryManager) ApplyAtomic(transaction ITransaction, categoryEntity ICategoryEntity, repositoryHandler func(IRepositoryTransaction, ICategoryEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (ICategory, error) { + result, err := NewCategoryFromEntity(categoryEntity) + if err != nil { + return nil, err + } + + transaction.OnCommit(func() { + cacheHandler(result.Id(), result) + }) + + if err := repositoryHandler(transaction, categoryEntity, editor.Id()); err != nil { + return nil, err + } + + return result, nil +} + +func (manager *categoryManager) Find(id int64) ICategory { + if object, exists := manager.cache.Get(id); !exists { + return nil + } else { + return object.(ICategory) + } +} + +func (manager *categoryManager) ForEach(iterator CategoryIterator) { + manager.cache.ForEachValue(func(object ISystemObject) { + iterator(object.(ICategory)) + }) +} + +func (manager *categoryManager) Filter(predicate CategoryFilterPredicate) ICategoryCollection { + categories := NewCategories() + if predicate == nil { + return categories + } + + manager.ForEach(func(category ICategory) { + if predicate(category) { + categories.Append(category) + } + }) + + return categories +} + +func (manager *categoryManager) Map(predicate CategoryMapPredicate) ICategoryCollection { + categories := NewCategories() + if predicate == nil { + return categories + } + + manager.ForEach(func(category ICategory) { + categories.Append(predicate(category)) + }) + + return categories +} + +func (manager *categoryManager) ListCategoriesByCategoryType(categoryTypeId int64, _ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) ICategoryCollection { + return manager.Filter(func(category ICategory) bool { + return category.CategoryTypeId() == categoryTypeId + }) +} + +func (manager *categoryManager) ForEachByCategoryType(categoryTypeId int64, iterator CategoryIterator) { + manager.ForEach(func(category ICategory) { + if category.CategoryTypeId() == categoryTypeId { + iterator(category) + } + }) +} + +func (manager *categoryManager) ListCategoriesByCategory(categoryId int64, _ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) ICategoryCollection { + return manager.Filter(func(category ICategory) bool { + return category.CategoryId() == categoryId + }) +} + +func (manager *categoryManager) ForEachByCategory(categoryId int64, iterator CategoryIterator) { + manager.ForEach(func(category ICategory) { + if category.CategoryId() == categoryId { + iterator(category) + } + }) +} diff --git a/greataped/components/core/category_manager_test.go b/greataped/components/core/category_manager_test.go new file mode 100644 index 0000000..d9ea038 --- /dev/null +++ b/greataped/components/core/category_manager_test.go @@ -0,0 +1,178 @@ +package core_test + +import ( + "testing" + + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/core" +) + +func TestCategoryManager_GetName(test *testing.T) { + manager := Conductor.CategoryManager() + + if manager.Name() != CATEGORY_MANAGER { + test.Fail() + } +} + +func TestCategoryManager_ResolveDependencies(test *testing.T) { + manager := Conductor.CategoryManager() + + if err := manager.ResolveDependencies(); err != nil { + test.Fatal(err) + } +} + +func TestCategoryManager_Load(test *testing.T) { + manager := Conductor.CategoryManager() + + if err := manager.Load(); err != nil { + test.Fatal(err) + } +} + +func TestCategoryManager_Reload(test *testing.T) { + manager := Conductor.CategoryManager() + + if err := manager.Reload(); err != nil && err != ERROR_OPERATION_NOT_SUPPORTED { + test.Fatal(err) + } +} + +func TestCategoryManager_Count(test *testing.T) { + manager := Conductor.CategoryManager() + + _ = manager.Count() +} + +func TestCategoryManager_Exists(test *testing.T) { + manager := Conductor.CategoryManager() + + if manager.Exists(0) { + test.FailNow() + } +} + +func TestCategoryManager_ListCategories(test *testing.T) { + manager := Conductor.CategoryManager() + + _ = manager.ListCategories(0, 0, "", nil) +} + +func TestCategoryManager_GetCategory(test *testing.T) { + manager := Conductor.CategoryManager() + + if category, err := manager.GetCategory(0, nil); err == nil { + _ = category + test.FailNow() + } +} + +func TestCategoryManager_AddCategory(test *testing.T) { + manager := Conductor.CategoryManager() + + category, err := manager.AddCategory(0, 0, "title", "description", nil) + if err != nil { + test.Fatal(err) + } + + _ = category +} + +func TestCategoryManager_UpdateCategory(test *testing.T) { + manager := Conductor.CategoryManager() + + category, err := manager.UpdateCategory(0, 0, 0, "title", "description", nil) + if err != nil { + test.Fatal(err) + } + + _ = category +} + +func TestCategoryManager_RemoveCategory(test *testing.T) { + manager := Conductor.CategoryManager() + + category, err := manager.RemoveCategory(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = category +} + +func TestCategoryManager_Find(test *testing.T) { + manager := Conductor.CategoryManager() + + category := manager.Find(0) + if category == nil { + test.Fail() + } + + _ = category +} + +func TestCategoryManager_ForEach(test *testing.T) { + manager := Conductor.CategoryManager() + + manager.ForEach(func(category ICategory) { + _ = category + }) +} + +func TestCategoryManager_Filter(test *testing.T) { + manager := Conductor.CategoryManager() + + categories := manager.Filter(func(category ICategory) bool { + return category.Id() < 0 + }) + + if categories.IsNotEmpty() { + test.Fail() + } + + _ = categories +} + +func TestCategoryManager_Map(test *testing.T) { + manager := Conductor.CategoryManager() + + categories := manager.Map(func(category ICategory) ICategory { + return category + }) + + if categories.Count() != manager.Count() { + test.Fail() + } + + _ = categories +} + +func TestCategoryManager_ListCategoriesByCategoryType(test *testing.T) { + manager := Conductor.CategoryManager() + + _ = manager.ListCategoriesByCategoryType(0, 0, 0, "", nil) +} + +func TestCategoryManager_ForEachByCategoryType(test *testing.T) { + manager := Conductor.CategoryManager() + + manager.ForEachByCategoryType(0, func(category ICategory) { + _ = category + }) +} + +func TestCategoryManager_ListCategoriesByCategory(test *testing.T) { + manager := Conductor.CategoryManager() + + _ = manager.ListCategoriesByCategory(0, 0, 0, "", nil) +} + +func TestCategoryManager_ForEachByCategory(test *testing.T) { + manager := Conductor.CategoryManager() + + manager.ForEachByCategory(0, func(category ICategory) { + _ = category + }) +} diff --git a/greataped/components/core/category_type.go b/greataped/components/core/category_type.go new file mode 100644 index 0000000..bcf85f9 --- /dev/null +++ b/greataped/components/core/category_type.go @@ -0,0 +1,281 @@ +package core + +import ( + "fmt" + + . "github.com/xeronith/diamante/contracts/security" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + "rail.town/infrastructure/components/model/repository" +) + +type categoryType struct { + object + description string +} + +// noinspection GoUnusedExportedFunction +func InitializeCategoryType() { + _ = ENABLE_SECURITY + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + _ = repository.Initialize +} + +func NewCategoryType(id int64, description string) (ICategoryType, error) { + instance := &categoryType{ + object: object{ + id: id, + }, + description: description, + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func NewCategoryTypeFromEntity(entity ICategoryTypeEntity) (ICategoryType, error) { + instance := &categoryType{ + object: object{ + id: entity.Id(), + }, + description: entity.Description(), + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func (categoryType *categoryType) Description() string { + return categoryType.description +} + +func (categoryType *categoryType) UpdateDescription(description string, editor Identity) { + if err := repository.CategoryTypes.UpdateDescription(categoryType.id, description, editor.Id()); err != nil { + panic(err.Error()) + } + + categoryType.description = description +} + +func (categoryType *categoryType) UpdateDescriptionAtomic(transaction ITransaction, description string, editor Identity) { + transaction.OnCommit(func() { + categoryType.description = description + }) + + if err := repository.CategoryTypes.UpdateDescriptionAtomic(transaction, categoryType.id, description, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (categoryType *categoryType) Validate() error { + return nil +} + +func (categoryType *categoryType) String() string { + return fmt.Sprintf("CategoryType (Id: %d, Description: %v)", categoryType.Id(), categoryType.Description()) +} + +//------------------------------------------------------------------------------ + +type categoryTypes struct { + collection CategoryTypes +} + +// NewCategoryTypes creates an empty collection of 'Category Type' which is not thread-safe. +func NewCategoryTypes() ICategoryTypeCollection { + return &categoryTypes{ + collection: make(CategoryTypes, 0), + } +} + +func (categoryTypes *categoryTypes) Count() int { + return len(categoryTypes.collection) +} + +func (categoryTypes *categoryTypes) IsEmpty() bool { + return len(categoryTypes.collection) == 0 +} + +func (categoryTypes *categoryTypes) IsNotEmpty() bool { + return len(categoryTypes.collection) > 0 +} + +func (categoryTypes *categoryTypes) HasExactlyOneItem() bool { + return len(categoryTypes.collection) == 1 +} + +func (categoryTypes *categoryTypes) HasAtLeastOneItem() bool { + return len(categoryTypes.collection) >= 1 +} + +func (categoryTypes *categoryTypes) First() ICategoryType { + return categoryTypes.collection[0] +} + +func (categoryTypes *categoryTypes) Append(categoryType ICategoryType) { + categoryTypes.collection = append(categoryTypes.collection, categoryType) +} + +func (categoryTypes *categoryTypes) ForEach(iterator CategoryTypeIterator) { + if iterator == nil { + return + } + + for _, value := range categoryTypes.collection { + iterator(value) + } +} + +func (categoryTypes *categoryTypes) Array() CategoryTypes { + return categoryTypes.collection +} + +//------------------------------------------------------------------------------ + +func (dispatcher *dispatcher) CategoryTypeExists(id int64) bool { + return dispatcher.conductor.CategoryTypeManager().Exists(id) +} + +func (dispatcher *dispatcher) CategoryTypeExistsWhich(condition CategoryTypeCondition) bool { + return dispatcher.conductor.CategoryTypeManager().ExistsWhich(condition) +} + +func (dispatcher *dispatcher) ListCategoryTypes() ICategoryTypeCollection { + return dispatcher.conductor.CategoryTypeManager().ListCategoryTypes(0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachCategoryType(iterator CategoryTypeIterator) { + dispatcher.conductor.CategoryTypeManager().ForEach(iterator) +} + +func (dispatcher *dispatcher) FilterCategoryTypes(predicate CategoryTypeFilterPredicate) ICategoryTypeCollection { + return dispatcher.conductor.CategoryTypeManager().Filter(predicate) +} + +func (dispatcher *dispatcher) MapCategoryTypes(predicate CategoryTypeMapPredicate) ICategoryTypeCollection { + return dispatcher.conductor.CategoryTypeManager().Map(predicate) +} + +func (dispatcher *dispatcher) GetCategoryType(id int64) ICategoryType { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().GetCategoryType(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } +} + +func (dispatcher *dispatcher) AddCategoryType(description string) ICategoryType { + transaction := dispatcher.transaction + if transaction != nil { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().AddCategoryTypeAtomic(transaction, description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } else { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().AddCategoryType(description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } +} + +func (dispatcher *dispatcher) AddCategoryTypeWithCustomId(id int64, description string) ICategoryType { + transaction := dispatcher.transaction + if transaction != nil { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().AddCategoryTypeWithCustomIdAtomic(id, transaction, description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } else { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().AddCategoryTypeWithCustomId(id, description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } +} + +func (dispatcher *dispatcher) LogCategoryType(description string, source string, payload string) { + dispatcher.conductor.CategoryTypeManager().Log(description, source, dispatcher.identity, payload) +} + +func (dispatcher *dispatcher) UpdateCategoryType(id int64, description string) ICategoryType { + transaction := dispatcher.transaction + if transaction != nil { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().UpdateCategoryTypeAtomic(transaction, id, description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } else { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().UpdateCategoryType(id, description, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } +} + +// noinspection GoUnusedParameter +func (dispatcher *dispatcher) UpdateCategoryTypeObject(object IObject, categoryType ICategoryType) ICategoryType { + transaction := dispatcher.transaction + if transaction != nil { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().UpdateCategoryTypeAtomic(transaction, object.Id(), categoryType.Description(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } else { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().UpdateCategoryType(object.Id(), categoryType.Description(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } +} + +func (dispatcher *dispatcher) AddOrUpdateCategoryTypeObject(object IObject, categoryType ICategoryType) ICategoryType { + transaction := dispatcher.transaction + if transaction != nil { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().AddOrUpdateCategoryTypeObjectAtomic(transaction, object.Id(), categoryType, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } else { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().AddOrUpdateCategoryTypeObject(object.Id(), categoryType, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } +} + +func (dispatcher *dispatcher) RemoveCategoryType(id int64) ICategoryType { + transaction := dispatcher.transaction + if transaction != nil { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().RemoveCategoryTypeAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } else { + if categoryType, err := dispatcher.conductor.CategoryTypeManager().RemoveCategoryType(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return categoryType + } + } +} diff --git a/greataped/components/core/category_type_manager.go b/greataped/components/core/category_type_manager.go new file mode 100644 index 0000000..1ed9fa7 --- /dev/null +++ b/greataped/components/core/category_type_manager.go @@ -0,0 +1,266 @@ +package core + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/system" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" + "rail.town/infrastructure/components/model/repository" +) + +// noinspection GoSnakeCaseUsage +const CATEGORY_TYPE_MANAGER = "CategoryTypeManager" + +type categoryTypeManager struct { + systemComponent + cache ICache +} + +func newCategoryTypeManager(configuration IConfiguration, logger ILogger, dependencies ...ISystemComponent) ICategoryTypeManager { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + + manager := &categoryTypeManager{ + systemComponent: newSystemComponent(configuration, logger), + cache: NewCache(), + } + + if err := manager.ResolveDependencies(dependencies...); err != nil { + return nil + } + + return manager +} + +func (manager *categoryTypeManager) Name() string { + return CATEGORY_TYPE_MANAGER +} + +func (manager *categoryTypeManager) ResolveDependencies(_ ...ISystemComponent) error { + return nil +} + +func (manager *categoryTypeManager) Load() error { + categoryTypeEntities, err := repository.CategoryTypes.FetchAll() + if err != nil { + return err + } + + categoryTypes := make(SystemObjectCache) + for _, categoryTypeEntity := range categoryTypeEntities { + if categoryType, err := NewCategoryTypeFromEntity(categoryTypeEntity); err == nil { + categoryTypes[categoryType.Id()] = categoryType + } else { + return err + } + } + + manager.cache.Load(categoryTypes) + return nil +} + +func (manager *categoryTypeManager) Reload() error { + return manager.Load() +} + +func (manager *categoryTypeManager) OnCacheChanged(callback CategoryTypeCacheCallback) { + manager.cache.OnChanged(callback) +} + +func (manager *categoryTypeManager) Count() int { + return manager.cache.Size() +} + +func (manager *categoryTypeManager) Exists(id int64) bool { + return manager.Find(id) != nil +} + +func (manager *categoryTypeManager) ExistsWhich(condition CategoryTypeCondition) bool { + var categoryTypes CategoryTypes + manager.ForEach(func(categoryType ICategoryType) { + if condition(categoryType) { + categoryTypes = append(categoryTypes, categoryType) + } + }) + + return len(categoryTypes) > 0 +} + +func (manager *categoryTypeManager) ListCategoryTypes(_ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) ICategoryTypeCollection { + return manager.Filter(CategoryTypePassThroughFilter) +} + +func (manager *categoryTypeManager) GetCategoryType(id int64, _ Identity) (ICategoryType, error) { + if categoryType := manager.Find(id); categoryType == nil { + return nil, ERROR_CATEGORY_TYPE_NOT_FOUND + } else { + return categoryType, nil + } +} + +func (manager *categoryTypeManager) AddCategoryType(description string, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(manager.UniqueId(), description) + return manager.Apply(categoryTypeEntity, repository.CategoryTypes.Add, manager.cache.Put, editor) +} + +func (manager *categoryTypeManager) AddCategoryTypeWithCustomId(id int64, description string, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(id, description) + return manager.Apply(categoryTypeEntity, repository.CategoryTypes.Add, manager.cache.Put, editor) +} + +func (manager *categoryTypeManager) AddCategoryTypeObject(categoryType ICategoryType, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(manager.UniqueId(), categoryType.Description()) + return manager.Apply(categoryTypeEntity, repository.CategoryTypes.Add, manager.cache.Put, editor) +} + +func (manager *categoryTypeManager) AddCategoryTypeAtomic(transaction ITransaction, description string, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(manager.UniqueId(), description) + return manager.ApplyAtomic(transaction, categoryTypeEntity, repository.CategoryTypes.AddAtomic, manager.cache.Put, editor) +} + +func (manager *categoryTypeManager) AddCategoryTypeWithCustomIdAtomic(id int64, transaction ITransaction, description string, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(id, description) + return manager.ApplyAtomic(transaction, categoryTypeEntity, repository.CategoryTypes.AddAtomic, manager.cache.Put, editor) +} + +func (manager *categoryTypeManager) AddCategoryTypeObjectAtomic(transaction ITransaction, categoryType ICategoryType, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(manager.UniqueId(), categoryType.Description()) + return manager.ApplyAtomic(transaction, categoryTypeEntity, repository.CategoryTypes.AddAtomic, manager.cache.Put, editor) +} + +func (manager *categoryTypeManager) Log(description string, source string, editor Identity, payload string) { + categoryTypePipeEntity := NewCategoryTypePipeEntity(manager.UniqueId(), description, source, editor.Id(), payload) + repository.Pipe.Insert(categoryTypePipeEntity) + + categoryType, err := NewCategoryTypeFromEntity(categoryTypePipeEntity) + if err != nil { + manager.Logger().Error(err) + } else { + manager.cache.Put(categoryType.Id(), categoryType) + } +} + +func (manager *categoryTypeManager) UpdateCategoryType(id int64, description string, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(id, description) + return manager.Apply(categoryTypeEntity, repository.CategoryTypes.Update, manager.cache.Put, editor) +} + +func (manager *categoryTypeManager) UpdateCategoryTypeObject(id int64, categoryType ICategoryType, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(id, categoryType.Description()) + return manager.Apply(categoryTypeEntity, repository.CategoryTypes.Update, manager.cache.Put, editor) +} + +func (manager *categoryTypeManager) UpdateCategoryTypeAtomic(transaction ITransaction, id int64, description string, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(id, description) + return manager.ApplyAtomic(transaction, categoryTypeEntity, repository.CategoryTypes.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *categoryTypeManager) UpdateCategoryTypeObjectAtomic(transaction ITransaction, id int64, categoryType ICategoryType, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(id, categoryType.Description()) + return manager.ApplyAtomic(transaction, categoryTypeEntity, repository.CategoryTypes.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *categoryTypeManager) AddOrUpdateCategoryTypeObject(id int64, categoryType ICategoryType, editor Identity) (ICategoryType, error) { + if manager.Exists(id) { + return manager.UpdateCategoryTypeObject(id, categoryType, editor) + } else { + return manager.AddCategoryTypeObject(categoryType, editor) + } +} + +func (manager *categoryTypeManager) AddOrUpdateCategoryTypeObjectAtomic(transaction ITransaction, id int64, categoryType ICategoryType, editor Identity) (ICategoryType, error) { + if manager.Exists(id) { + return manager.UpdateCategoryTypeObjectAtomic(transaction, id, categoryType, editor) + } else { + return manager.AddCategoryTypeObjectAtomic(transaction, categoryType, editor) + } +} + +func (manager *categoryTypeManager) RemoveCategoryType(id int64, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(id, "") + return manager.Apply(categoryTypeEntity, repository.CategoryTypes.Remove, manager.cache.Remove, editor) +} + +func (manager *categoryTypeManager) RemoveCategoryTypeAtomic(transaction ITransaction, id int64, editor Identity) (ICategoryType, error) { + categoryTypeEntity := NewCategoryTypeEntity(id, "") + return manager.ApplyAtomic(transaction, categoryTypeEntity, repository.CategoryTypes.RemoveAtomic, manager.cache.Remove, editor) +} + +func (manager *categoryTypeManager) Apply(categoryTypeEntity ICategoryTypeEntity, repositoryHandler func(ICategoryTypeEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (ICategoryType, error) { + result, err := NewCategoryTypeFromEntity(categoryTypeEntity) + if err != nil { + return nil, err + } + + if err := repositoryHandler(categoryTypeEntity, editor.Id()); err != nil { + return nil, err + } + + cacheHandler(result.Id(), result) + return result, nil +} + +func (manager *categoryTypeManager) ApplyAtomic(transaction ITransaction, categoryTypeEntity ICategoryTypeEntity, repositoryHandler func(IRepositoryTransaction, ICategoryTypeEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (ICategoryType, error) { + result, err := NewCategoryTypeFromEntity(categoryTypeEntity) + if err != nil { + return nil, err + } + + transaction.OnCommit(func() { + cacheHandler(result.Id(), result) + }) + + if err := repositoryHandler(transaction, categoryTypeEntity, editor.Id()); err != nil { + return nil, err + } + + return result, nil +} + +func (manager *categoryTypeManager) Find(id int64) ICategoryType { + if object, exists := manager.cache.Get(id); !exists { + return nil + } else { + return object.(ICategoryType) + } +} + +func (manager *categoryTypeManager) ForEach(iterator CategoryTypeIterator) { + manager.cache.ForEachValue(func(object ISystemObject) { + iterator(object.(ICategoryType)) + }) +} + +func (manager *categoryTypeManager) Filter(predicate CategoryTypeFilterPredicate) ICategoryTypeCollection { + categoryTypes := NewCategoryTypes() + if predicate == nil { + return categoryTypes + } + + manager.ForEach(func(categoryType ICategoryType) { + if predicate(categoryType) { + categoryTypes.Append(categoryType) + } + }) + + return categoryTypes +} + +func (manager *categoryTypeManager) Map(predicate CategoryTypeMapPredicate) ICategoryTypeCollection { + categoryTypes := NewCategoryTypes() + if predicate == nil { + return categoryTypes + } + + manager.ForEach(func(categoryType ICategoryType) { + categoryTypes.Append(predicate(categoryType)) + }) + + return categoryTypes +} diff --git a/greataped/components/core/category_type_manager_test.go b/greataped/components/core/category_type_manager_test.go new file mode 100644 index 0000000..1ab1547 --- /dev/null +++ b/greataped/components/core/category_type_manager_test.go @@ -0,0 +1,150 @@ +package core_test + +import ( + "testing" + + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/core" +) + +func TestCategoryTypeManager_GetName(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + if manager.Name() != CATEGORY_TYPE_MANAGER { + test.Fail() + } +} + +func TestCategoryTypeManager_ResolveDependencies(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + if err := manager.ResolveDependencies(); err != nil { + test.Fatal(err) + } +} + +func TestCategoryTypeManager_Load(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + if err := manager.Load(); err != nil { + test.Fatal(err) + } +} + +func TestCategoryTypeManager_Reload(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + if err := manager.Reload(); err != nil && err != ERROR_OPERATION_NOT_SUPPORTED { + test.Fatal(err) + } +} + +func TestCategoryTypeManager_Count(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + _ = manager.Count() +} + +func TestCategoryTypeManager_Exists(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + if manager.Exists(0) { + test.FailNow() + } +} + +func TestCategoryTypeManager_ListCategoryTypes(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + _ = manager.ListCategoryTypes(0, 0, "", nil) +} + +func TestCategoryTypeManager_GetCategoryType(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + if categoryType, err := manager.GetCategoryType(0, nil); err == nil { + _ = categoryType + test.FailNow() + } +} + +func TestCategoryTypeManager_AddCategoryType(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + categoryType, err := manager.AddCategoryType("description", nil) + if err != nil { + test.Fatal(err) + } + + _ = categoryType +} + +func TestCategoryTypeManager_UpdateCategoryType(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + categoryType, err := manager.UpdateCategoryType(0, "description", nil) + if err != nil { + test.Fatal(err) + } + + _ = categoryType +} + +func TestCategoryTypeManager_RemoveCategoryType(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + categoryType, err := manager.RemoveCategoryType(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = categoryType +} + +func TestCategoryTypeManager_Find(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + categoryType := manager.Find(0) + if categoryType == nil { + test.Fail() + } + + _ = categoryType +} + +func TestCategoryTypeManager_ForEach(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + manager.ForEach(func(categoryType ICategoryType) { + _ = categoryType + }) +} + +func TestCategoryTypeManager_Filter(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + categoryTypes := manager.Filter(func(categoryType ICategoryType) bool { + return categoryType.Id() < 0 + }) + + if categoryTypes.IsNotEmpty() { + test.Fail() + } + + _ = categoryTypes +} + +func TestCategoryTypeManager_Map(test *testing.T) { + manager := Conductor.CategoryTypeManager() + + categoryTypes := manager.Map(func(categoryType ICategoryType) ICategoryType { + return categoryType + }) + + if categoryTypes.Count() != manager.Count() { + test.Fail() + } + + _ = categoryTypes +} diff --git a/greataped/components/core/collections.go b/greataped/components/core/collections.go new file mode 100644 index 0000000..860092b --- /dev/null +++ b/greataped/components/core/collections.go @@ -0,0 +1,47 @@ +package core + +import . "rail.town/infrastructure/components/contracts" + +//region IDispatcher Implementation + +func (dispatcher *dispatcher) NewDocuments() IDocumentCollection { + return NewDocuments() +} + +func (dispatcher *dispatcher) NewSystemSchedules() ISystemScheduleCollection { + return NewSystemSchedules() +} + +func (dispatcher *dispatcher) NewIdentities() IIdentityCollection { + return NewIdentities() +} + +func (dispatcher *dispatcher) NewAccessControls() IAccessControlCollection { + return NewAccessControls() +} + +func (dispatcher *dispatcher) NewRemoteActivities() IRemoteActivityCollection { + return NewRemoteActivities() +} + +func (dispatcher *dispatcher) NewCategoryTypes() ICategoryTypeCollection { + return NewCategoryTypes() +} + +func (dispatcher *dispatcher) NewCategories() ICategoryCollection { + return NewCategories() +} + +func (dispatcher *dispatcher) NewUsers() IUserCollection { + return NewUsers() +} + +func (dispatcher *dispatcher) NewSpis() ISpiCollection { + return NewSpis() +} + +func (dispatcher *dispatcher) NewCustomErrors() ICustomErrorCollection { + return NewCustomErrors() +} + +//endregion diff --git a/greataped/components/core/custom_error.go b/greataped/components/core/custom_error.go new file mode 100644 index 0000000..6f28e76 --- /dev/null +++ b/greataped/components/core/custom_error.go @@ -0,0 +1,240 @@ +package core + +import ( + "fmt" + + . "github.com/xeronith/diamante/contracts/security" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + "rail.town/infrastructure/components/model/repository" +) + +type customError struct { +} + +// noinspection GoUnusedExportedFunction +func InitializeCustomError() { + _ = ENABLE_SECURITY + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + _ = repository.Initialize +} + +func NewCustomError() (ICustomError, error) { + instance := &customError{} + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func (customError *customError) Validate() error { + return nil +} + +func (customError *customError) String() string { + return fmt.Sprintf("CustomError (Id: %d)", 0) +} + +//------------------------------------------------------------------------------ + +type customErrors struct { + collection CustomErrors +} + +// NewCustomErrors creates an empty collection of 'Custom Error' which is not thread-safe. +func NewCustomErrors() ICustomErrorCollection { + return &customErrors{ + collection: make(CustomErrors, 0), + } +} + +func (customErrors *customErrors) Count() int { + return len(customErrors.collection) +} + +func (customErrors *customErrors) IsEmpty() bool { + return len(customErrors.collection) == 0 +} + +func (customErrors *customErrors) IsNotEmpty() bool { + return len(customErrors.collection) > 0 +} + +func (customErrors *customErrors) HasExactlyOneItem() bool { + return len(customErrors.collection) == 1 +} + +func (customErrors *customErrors) HasAtLeastOneItem() bool { + return len(customErrors.collection) >= 1 +} + +func (customErrors *customErrors) First() ICustomError { + return customErrors.collection[0] +} + +func (customErrors *customErrors) Append(customError ICustomError) { + customErrors.collection = append(customErrors.collection, customError) +} + +func (customErrors *customErrors) ForEach(iterator CustomErrorIterator) { + if iterator == nil { + return + } + + for _, value := range customErrors.collection { + iterator(value) + } +} + +func (customErrors *customErrors) Array() CustomErrors { + return customErrors.collection +} + +//------------------------------------------------------------------------------ + +func (dispatcher *dispatcher) CustomErrorExists(id int64) bool { + return dispatcher.conductor.CustomErrorManager().Exists(id) +} + +func (dispatcher *dispatcher) CustomErrorExistsWhich(condition CustomErrorCondition) bool { + return dispatcher.conductor.CustomErrorManager().ExistsWhich(condition) +} + +func (dispatcher *dispatcher) ListCustomErrors() ICustomErrorCollection { + return dispatcher.conductor.CustomErrorManager().ListCustomErrors(0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachCustomError(iterator CustomErrorIterator) { + dispatcher.conductor.CustomErrorManager().ForEach(iterator) +} + +func (dispatcher *dispatcher) FilterCustomErrors(predicate CustomErrorFilterPredicate) ICustomErrorCollection { + return dispatcher.conductor.CustomErrorManager().Filter(predicate) +} + +func (dispatcher *dispatcher) MapCustomErrors(predicate CustomErrorMapPredicate) ICustomErrorCollection { + return dispatcher.conductor.CustomErrorManager().Map(predicate) +} + +func (dispatcher *dispatcher) GetCustomError(id int64) ICustomError { + if customError, err := dispatcher.conductor.CustomErrorManager().GetCustomError(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } +} + +func (dispatcher *dispatcher) AddCustomError() ICustomError { + transaction := dispatcher.transaction + if transaction != nil { + if customError, err := dispatcher.conductor.CustomErrorManager().AddCustomErrorAtomic(transaction, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } else { + if customError, err := dispatcher.conductor.CustomErrorManager().AddCustomError(dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } +} + +func (dispatcher *dispatcher) AddCustomErrorWithCustomId(id int64) ICustomError { + transaction := dispatcher.transaction + if transaction != nil { + if customError, err := dispatcher.conductor.CustomErrorManager().AddCustomErrorWithCustomIdAtomic(id, transaction, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } else { + if customError, err := dispatcher.conductor.CustomErrorManager().AddCustomErrorWithCustomId(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } +} + +func (dispatcher *dispatcher) LogCustomError(source string, payload string) { + dispatcher.conductor.CustomErrorManager().Log(source, dispatcher.identity, payload) +} + +func (dispatcher *dispatcher) UpdateCustomError(id int64) ICustomError { + transaction := dispatcher.transaction + if transaction != nil { + if customError, err := dispatcher.conductor.CustomErrorManager().UpdateCustomErrorAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } else { + if customError, err := dispatcher.conductor.CustomErrorManager().UpdateCustomError(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } +} + +// noinspection GoUnusedParameter +func (dispatcher *dispatcher) UpdateCustomErrorObject(object IObject, customError ICustomError) ICustomError { + transaction := dispatcher.transaction + if transaction != nil { + if customError, err := dispatcher.conductor.CustomErrorManager().UpdateCustomErrorAtomic(transaction, object.Id(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } else { + if customError, err := dispatcher.conductor.CustomErrorManager().UpdateCustomError(object.Id(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } +} + +func (dispatcher *dispatcher) AddOrUpdateCustomErrorObject(object IObject, customError ICustomError) ICustomError { + transaction := dispatcher.transaction + if transaction != nil { + if customError, err := dispatcher.conductor.CustomErrorManager().AddOrUpdateCustomErrorObjectAtomic(transaction, object.Id(), customError, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } else { + if customError, err := dispatcher.conductor.CustomErrorManager().AddOrUpdateCustomErrorObject(object.Id(), customError, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } +} + +func (dispatcher *dispatcher) RemoveCustomError(id int64) ICustomError { + transaction := dispatcher.transaction + if transaction != nil { + if customError, err := dispatcher.conductor.CustomErrorManager().RemoveCustomErrorAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } else { + if customError, err := dispatcher.conductor.CustomErrorManager().RemoveCustomError(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return customError + } + } +} + +func (dispatcher *dispatcher) ResolveError(document IDocument) (IResolveErrorResult, error) { + return dispatcher.conductor.CustomErrorManager().ResolveError(document, dispatcher.identity) +} diff --git a/greataped/components/core/custom_error_manager.go b/greataped/components/core/custom_error_manager.go new file mode 100644 index 0000000..2dfed70 --- /dev/null +++ b/greataped/components/core/custom_error_manager.go @@ -0,0 +1,211 @@ +package core + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/system" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" +) + +// noinspection GoSnakeCaseUsage +const CUSTOM_ERROR_MANAGER = "CustomErrorManager" + +type customErrorManager struct { + systemComponent + cache ICache +} + +func newCustomErrorManager(configuration IConfiguration, logger ILogger, dependencies ...ISystemComponent) ICustomErrorManager { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + + manager := &customErrorManager{ + systemComponent: newSystemComponent(configuration, logger), + cache: NewCache(), + } + + if err := manager.ResolveDependencies(dependencies...); err != nil { + return nil + } + + return manager +} + +func (manager *customErrorManager) Name() string { + return CUSTOM_ERROR_MANAGER +} + +func (manager *customErrorManager) ResolveDependencies(_ ...ISystemComponent) error { + return nil +} + +func (manager *customErrorManager) Load() error { + return nil +} + +func (manager *customErrorManager) Reload() error { + return manager.Load() +} + +func (manager *customErrorManager) OnCacheChanged(callback CustomErrorCacheCallback) { + manager.cache.OnChanged(callback) +} + +func (manager *customErrorManager) Count() int { + return manager.cache.Size() +} + +func (manager *customErrorManager) Exists(id int64) bool { + return manager.Find(id) != nil +} + +func (manager *customErrorManager) ExistsWhich(condition CustomErrorCondition) bool { + var customErrors CustomErrors + manager.ForEach(func(customError ICustomError) { + if condition(customError) { + customErrors = append(customErrors, customError) + } + }) + + return len(customErrors) > 0 +} + +func (manager *customErrorManager) ListCustomErrors(_ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) ICustomErrorCollection { + return manager.Filter(CustomErrorPassThroughFilter) +} + +func (manager *customErrorManager) GetCustomError(id int64, _ Identity) (ICustomError, error) { + if customError := manager.Find(id); customError == nil { + return nil, ERROR_CUSTOM_ERROR_NOT_FOUND + } else { + return customError, nil + } +} + +func (manager *customErrorManager) AddCustomError(editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) AddCustomErrorWithCustomId(id int64, editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) AddCustomErrorObject(customError ICustomError, editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) AddCustomErrorAtomic(transaction ITransaction, editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) AddCustomErrorWithCustomIdAtomic(id int64, transaction ITransaction, editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) AddCustomErrorObjectAtomic(transaction ITransaction, customError ICustomError, editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) Log(source string, editor Identity, payload string) { +} + +func (manager *customErrorManager) UpdateCustomError(id int64, editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) UpdateCustomErrorObject(id int64, customError ICustomError, editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) UpdateCustomErrorAtomic(transaction ITransaction, id int64, editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) UpdateCustomErrorObjectAtomic(transaction ITransaction, id int64, customError ICustomError, editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) AddOrUpdateCustomErrorObject(id int64, customError ICustomError, editor Identity) (ICustomError, error) { + if manager.Exists(id) { + return manager.UpdateCustomErrorObject(id, customError, editor) + } else { + return manager.AddCustomErrorObject(customError, editor) + } +} + +func (manager *customErrorManager) AddOrUpdateCustomErrorObjectAtomic(transaction ITransaction, id int64, customError ICustomError, editor Identity) (ICustomError, error) { + if manager.Exists(id) { + return manager.UpdateCustomErrorObjectAtomic(transaction, id, customError, editor) + } else { + return manager.AddCustomErrorObjectAtomic(transaction, customError, editor) + } +} + +func (manager *customErrorManager) RemoveCustomError(id int64, editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) RemoveCustomErrorAtomic(transaction ITransaction, id int64, editor Identity) (ICustomError, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *customErrorManager) Find(id int64) ICustomError { + if object, exists := manager.cache.Get(id); !exists { + return nil + } else { + return object.(ICustomError) + } +} + +func (manager *customErrorManager) ForEach(iterator CustomErrorIterator) { + manager.cache.ForEachValue(func(object ISystemObject) { + iterator(object.(ICustomError)) + }) +} + +func (manager *customErrorManager) Filter(predicate CustomErrorFilterPredicate) ICustomErrorCollection { + customErrors := NewCustomErrors() + if predicate == nil { + return customErrors + } + + manager.ForEach(func(customError ICustomError) { + if predicate(customError) { + customErrors.Append(customError) + } + }) + + return customErrors +} + +func (manager *customErrorManager) Map(predicate CustomErrorMapPredicate) ICustomErrorCollection { + customErrors := NewCustomErrors() + if predicate == nil { + return customErrors + } + + manager.ForEach(func(customError ICustomError) { + customErrors.Append(predicate(customError)) + }) + + return customErrors +} + +//region IResolveErrorResult Implementation + +type resolveErrorResult struct { +} + +func NewResolveErrorResult(_ interface{}) IResolveErrorResult { + return &resolveErrorResult{} +} + +//endregion + +func (manager *customErrorManager) ResolveError(document IDocument, editor Identity) (result IResolveErrorResult, err error) { + return nil, ERROR_NOT_IMPLEMENTED +} diff --git a/greataped/components/core/custom_error_manager_test.go b/greataped/components/core/custom_error_manager_test.go new file mode 100644 index 0000000..ec8e698 --- /dev/null +++ b/greataped/components/core/custom_error_manager_test.go @@ -0,0 +1,161 @@ +package core_test + +import ( + "testing" + + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/core" +) + +func TestCustomErrorManager_GetName(test *testing.T) { + manager := Conductor.CustomErrorManager() + + if manager.Name() != CUSTOM_ERROR_MANAGER { + test.Fail() + } +} + +func TestCustomErrorManager_ResolveDependencies(test *testing.T) { + manager := Conductor.CustomErrorManager() + + if err := manager.ResolveDependencies(); err != nil { + test.Fatal(err) + } +} + +func TestCustomErrorManager_Load(test *testing.T) { + manager := Conductor.CustomErrorManager() + + if err := manager.Load(); err != nil { + test.Fatal(err) + } +} + +func TestCustomErrorManager_Reload(test *testing.T) { + manager := Conductor.CustomErrorManager() + + if err := manager.Reload(); err != nil && err != ERROR_OPERATION_NOT_SUPPORTED { + test.Fatal(err) + } +} + +func TestCustomErrorManager_Count(test *testing.T) { + manager := Conductor.CustomErrorManager() + + _ = manager.Count() +} + +func TestCustomErrorManager_Exists(test *testing.T) { + manager := Conductor.CustomErrorManager() + + if manager.Exists(0) { + test.FailNow() + } +} + +func TestCustomErrorManager_ListCustomErrors(test *testing.T) { + manager := Conductor.CustomErrorManager() + + _ = manager.ListCustomErrors(0, 0, "", nil) +} + +func TestCustomErrorManager_GetCustomError(test *testing.T) { + manager := Conductor.CustomErrorManager() + + if customError, err := manager.GetCustomError(0, nil); err == nil { + _ = customError + test.FailNow() + } +} + +func TestCustomErrorManager_AddCustomError(test *testing.T) { + manager := Conductor.CustomErrorManager() + + customError, err := manager.AddCustomError(nil) + if err != nil { + test.Fatal(err) + } + + _ = customError +} + +func TestCustomErrorManager_UpdateCustomError(test *testing.T) { + manager := Conductor.CustomErrorManager() + + customError, err := manager.UpdateCustomError(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = customError +} + +func TestCustomErrorManager_RemoveCustomError(test *testing.T) { + manager := Conductor.CustomErrorManager() + + customError, err := manager.RemoveCustomError(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = customError +} + +func TestCustomErrorManager_Find(test *testing.T) { + manager := Conductor.CustomErrorManager() + + customError := manager.Find(0) + if customError == nil { + test.Fail() + } + + _ = customError +} + +func TestCustomErrorManager_ForEach(test *testing.T) { + manager := Conductor.CustomErrorManager() + + manager.ForEach(func(customError ICustomError) { + _ = customError + }) +} + +func TestCustomErrorManager_Filter(test *testing.T) { + manager := Conductor.CustomErrorManager() + + customErrors := manager.Filter(func(customError ICustomError) bool { + return false + }) + + if customErrors.IsNotEmpty() { + test.Fail() + } + + _ = customErrors +} + +func TestCustomErrorManager_Map(test *testing.T) { + manager := Conductor.CustomErrorManager() + + customErrors := manager.Map(func(customError ICustomError) ICustomError { + return customError + }) + + if customErrors.Count() != manager.Count() { + test.Fail() + } + + _ = customErrors +} + +func TestCustomErrorManager_ResolveError(test *testing.T) { + manager := Conductor.CustomErrorManager() + + result, err := manager.ResolveError(nil, nil) + if err != nil { + test.Fatal(err) + } + + _ = result +} diff --git a/greataped/components/core/document.go b/greataped/components/core/document.go new file mode 100644 index 0000000..24e1cb0 --- /dev/null +++ b/greataped/components/core/document.go @@ -0,0 +1,281 @@ +package core + +import ( + "fmt" + + . "github.com/xeronith/diamante/contracts/security" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + "rail.town/infrastructure/components/model/repository" +) + +type document struct { + object + content string +} + +// noinspection GoUnusedExportedFunction +func InitializeDocument() { + _ = ENABLE_SECURITY + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + _ = repository.Initialize +} + +func NewDocument(id int64, content string) (IDocument, error) { + instance := &document{ + object: object{ + id: id, + }, + content: content, + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func NewDocumentFromEntity(entity IDocumentEntity) (IDocument, error) { + instance := &document{ + object: object{ + id: entity.Id(), + }, + content: entity.Content(), + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func (document *document) Content() string { + return document.content +} + +func (document *document) UpdateContent(content string, editor Identity) { + if err := repository.Documents.UpdateContent(document.id, content, editor.Id()); err != nil { + panic(err.Error()) + } + + document.content = content +} + +func (document *document) UpdateContentAtomic(transaction ITransaction, content string, editor Identity) { + transaction.OnCommit(func() { + document.content = content + }) + + if err := repository.Documents.UpdateContentAtomic(transaction, document.id, content, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (document *document) Validate() error { + return nil +} + +func (document *document) String() string { + return fmt.Sprintf("Document (Id: %d, Content: %v)", document.Id(), document.Content()) +} + +//------------------------------------------------------------------------------ + +type documents struct { + collection Documents +} + +// NewDocuments creates an empty collection of 'Document' which is not thread-safe. +func NewDocuments() IDocumentCollection { + return &documents{ + collection: make(Documents, 0), + } +} + +func (documents *documents) Count() int { + return len(documents.collection) +} + +func (documents *documents) IsEmpty() bool { + return len(documents.collection) == 0 +} + +func (documents *documents) IsNotEmpty() bool { + return len(documents.collection) > 0 +} + +func (documents *documents) HasExactlyOneItem() bool { + return len(documents.collection) == 1 +} + +func (documents *documents) HasAtLeastOneItem() bool { + return len(documents.collection) >= 1 +} + +func (documents *documents) First() IDocument { + return documents.collection[0] +} + +func (documents *documents) Append(document IDocument) { + documents.collection = append(documents.collection, document) +} + +func (documents *documents) ForEach(iterator DocumentIterator) { + if iterator == nil { + return + } + + for _, value := range documents.collection { + iterator(value) + } +} + +func (documents *documents) Array() Documents { + return documents.collection +} + +//------------------------------------------------------------------------------ + +func (dispatcher *dispatcher) DocumentExists(id int64) bool { + return dispatcher.conductor.DocumentManager().Exists(id) +} + +func (dispatcher *dispatcher) DocumentExistsWhich(condition DocumentCondition) bool { + return dispatcher.conductor.DocumentManager().ExistsWhich(condition) +} + +func (dispatcher *dispatcher) ListDocuments() IDocumentCollection { + return dispatcher.conductor.DocumentManager().ListDocuments(0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachDocument(iterator DocumentIterator) { + dispatcher.conductor.DocumentManager().ForEach(iterator) +} + +func (dispatcher *dispatcher) FilterDocuments(predicate DocumentFilterPredicate) IDocumentCollection { + return dispatcher.conductor.DocumentManager().Filter(predicate) +} + +func (dispatcher *dispatcher) MapDocuments(predicate DocumentMapPredicate) IDocumentCollection { + return dispatcher.conductor.DocumentManager().Map(predicate) +} + +func (dispatcher *dispatcher) GetDocument(id int64) IDocument { + if document, err := dispatcher.conductor.DocumentManager().GetDocument(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } +} + +func (dispatcher *dispatcher) AddDocument(content string) IDocument { + transaction := dispatcher.transaction + if transaction != nil { + if document, err := dispatcher.conductor.DocumentManager().AddDocumentAtomic(transaction, content, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } else { + if document, err := dispatcher.conductor.DocumentManager().AddDocument(content, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } +} + +func (dispatcher *dispatcher) AddDocumentWithCustomId(id int64, content string) IDocument { + transaction := dispatcher.transaction + if transaction != nil { + if document, err := dispatcher.conductor.DocumentManager().AddDocumentWithCustomIdAtomic(id, transaction, content, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } else { + if document, err := dispatcher.conductor.DocumentManager().AddDocumentWithCustomId(id, content, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } +} + +func (dispatcher *dispatcher) LogDocument(content string, source string, payload string) { + dispatcher.conductor.DocumentManager().Log(content, source, dispatcher.identity, payload) +} + +func (dispatcher *dispatcher) UpdateDocument(id int64, content string) IDocument { + transaction := dispatcher.transaction + if transaction != nil { + if document, err := dispatcher.conductor.DocumentManager().UpdateDocumentAtomic(transaction, id, content, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } else { + if document, err := dispatcher.conductor.DocumentManager().UpdateDocument(id, content, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } +} + +// noinspection GoUnusedParameter +func (dispatcher *dispatcher) UpdateDocumentObject(object IObject, document IDocument) IDocument { + transaction := dispatcher.transaction + if transaction != nil { + if document, err := dispatcher.conductor.DocumentManager().UpdateDocumentAtomic(transaction, object.Id(), document.Content(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } else { + if document, err := dispatcher.conductor.DocumentManager().UpdateDocument(object.Id(), document.Content(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } +} + +func (dispatcher *dispatcher) AddOrUpdateDocumentObject(object IObject, document IDocument) IDocument { + transaction := dispatcher.transaction + if transaction != nil { + if document, err := dispatcher.conductor.DocumentManager().AddOrUpdateDocumentObjectAtomic(transaction, object.Id(), document, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } else { + if document, err := dispatcher.conductor.DocumentManager().AddOrUpdateDocumentObject(object.Id(), document, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } +} + +func (dispatcher *dispatcher) RemoveDocument(id int64) IDocument { + transaction := dispatcher.transaction + if transaction != nil { + if document, err := dispatcher.conductor.DocumentManager().RemoveDocumentAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } else { + if document, err := dispatcher.conductor.DocumentManager().RemoveDocument(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return document + } + } +} diff --git a/greataped/components/core/document_manager.go b/greataped/components/core/document_manager.go new file mode 100644 index 0000000..2f88d53 --- /dev/null +++ b/greataped/components/core/document_manager.go @@ -0,0 +1,266 @@ +package core + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/system" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" + "rail.town/infrastructure/components/model/repository" +) + +// noinspection GoSnakeCaseUsage +const DOCUMENT_MANAGER = "DocumentManager" + +type documentManager struct { + systemComponent + cache ICache +} + +func newDocumentManager(configuration IConfiguration, logger ILogger, dependencies ...ISystemComponent) IDocumentManager { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + + manager := &documentManager{ + systemComponent: newSystemComponent(configuration, logger), + cache: NewCache(), + } + + if err := manager.ResolveDependencies(dependencies...); err != nil { + return nil + } + + return manager +} + +func (manager *documentManager) Name() string { + return DOCUMENT_MANAGER +} + +func (manager *documentManager) ResolveDependencies(_ ...ISystemComponent) error { + return nil +} + +func (manager *documentManager) Load() error { + documentEntities, err := repository.Documents.FetchAll() + if err != nil { + return err + } + + documents := make(SystemObjectCache) + for _, documentEntity := range documentEntities { + if document, err := NewDocumentFromEntity(documentEntity); err == nil { + documents[document.Id()] = document + } else { + return err + } + } + + manager.cache.Load(documents) + return nil +} + +func (manager *documentManager) Reload() error { + return manager.Load() +} + +func (manager *documentManager) OnCacheChanged(callback DocumentCacheCallback) { + manager.cache.OnChanged(callback) +} + +func (manager *documentManager) Count() int { + return manager.cache.Size() +} + +func (manager *documentManager) Exists(id int64) bool { + return manager.Find(id) != nil +} + +func (manager *documentManager) ExistsWhich(condition DocumentCondition) bool { + var documents Documents + manager.ForEach(func(document IDocument) { + if condition(document) { + documents = append(documents, document) + } + }) + + return len(documents) > 0 +} + +func (manager *documentManager) ListDocuments(_ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) IDocumentCollection { + return manager.Filter(DocumentPassThroughFilter) +} + +func (manager *documentManager) GetDocument(id int64, _ Identity) (IDocument, error) { + if document := manager.Find(id); document == nil { + return nil, ERROR_DOCUMENT_NOT_FOUND + } else { + return document, nil + } +} + +func (manager *documentManager) AddDocument(content string, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(manager.UniqueId(), content) + return manager.Apply(documentEntity, repository.Documents.Add, manager.cache.Put, editor) +} + +func (manager *documentManager) AddDocumentWithCustomId(id int64, content string, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(id, content) + return manager.Apply(documentEntity, repository.Documents.Add, manager.cache.Put, editor) +} + +func (manager *documentManager) AddDocumentObject(document IDocument, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(manager.UniqueId(), document.Content()) + return manager.Apply(documentEntity, repository.Documents.Add, manager.cache.Put, editor) +} + +func (manager *documentManager) AddDocumentAtomic(transaction ITransaction, content string, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(manager.UniqueId(), content) + return manager.ApplyAtomic(transaction, documentEntity, repository.Documents.AddAtomic, manager.cache.Put, editor) +} + +func (manager *documentManager) AddDocumentWithCustomIdAtomic(id int64, transaction ITransaction, content string, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(id, content) + return manager.ApplyAtomic(transaction, documentEntity, repository.Documents.AddAtomic, manager.cache.Put, editor) +} + +func (manager *documentManager) AddDocumentObjectAtomic(transaction ITransaction, document IDocument, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(manager.UniqueId(), document.Content()) + return manager.ApplyAtomic(transaction, documentEntity, repository.Documents.AddAtomic, manager.cache.Put, editor) +} + +func (manager *documentManager) Log(content string, source string, editor Identity, payload string) { + documentPipeEntity := NewDocumentPipeEntity(manager.UniqueId(), content, source, editor.Id(), payload) + repository.Pipe.Insert(documentPipeEntity) + + document, err := NewDocumentFromEntity(documentPipeEntity) + if err != nil { + manager.Logger().Error(err) + } else { + manager.cache.Put(document.Id(), document) + } +} + +func (manager *documentManager) UpdateDocument(id int64, content string, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(id, content) + return manager.Apply(documentEntity, repository.Documents.Update, manager.cache.Put, editor) +} + +func (manager *documentManager) UpdateDocumentObject(id int64, document IDocument, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(id, document.Content()) + return manager.Apply(documentEntity, repository.Documents.Update, manager.cache.Put, editor) +} + +func (manager *documentManager) UpdateDocumentAtomic(transaction ITransaction, id int64, content string, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(id, content) + return manager.ApplyAtomic(transaction, documentEntity, repository.Documents.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *documentManager) UpdateDocumentObjectAtomic(transaction ITransaction, id int64, document IDocument, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(id, document.Content()) + return manager.ApplyAtomic(transaction, documentEntity, repository.Documents.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *documentManager) AddOrUpdateDocumentObject(id int64, document IDocument, editor Identity) (IDocument, error) { + if manager.Exists(id) { + return manager.UpdateDocumentObject(id, document, editor) + } else { + return manager.AddDocumentObject(document, editor) + } +} + +func (manager *documentManager) AddOrUpdateDocumentObjectAtomic(transaction ITransaction, id int64, document IDocument, editor Identity) (IDocument, error) { + if manager.Exists(id) { + return manager.UpdateDocumentObjectAtomic(transaction, id, document, editor) + } else { + return manager.AddDocumentObjectAtomic(transaction, document, editor) + } +} + +func (manager *documentManager) RemoveDocument(id int64, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(id, "") + return manager.Apply(documentEntity, repository.Documents.Remove, manager.cache.Remove, editor) +} + +func (manager *documentManager) RemoveDocumentAtomic(transaction ITransaction, id int64, editor Identity) (IDocument, error) { + documentEntity := NewDocumentEntity(id, "") + return manager.ApplyAtomic(transaction, documentEntity, repository.Documents.RemoveAtomic, manager.cache.Remove, editor) +} + +func (manager *documentManager) Apply(documentEntity IDocumentEntity, repositoryHandler func(IDocumentEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (IDocument, error) { + result, err := NewDocumentFromEntity(documentEntity) + if err != nil { + return nil, err + } + + if err := repositoryHandler(documentEntity, editor.Id()); err != nil { + return nil, err + } + + cacheHandler(result.Id(), result) + return result, nil +} + +func (manager *documentManager) ApplyAtomic(transaction ITransaction, documentEntity IDocumentEntity, repositoryHandler func(IRepositoryTransaction, IDocumentEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (IDocument, error) { + result, err := NewDocumentFromEntity(documentEntity) + if err != nil { + return nil, err + } + + transaction.OnCommit(func() { + cacheHandler(result.Id(), result) + }) + + if err := repositoryHandler(transaction, documentEntity, editor.Id()); err != nil { + return nil, err + } + + return result, nil +} + +func (manager *documentManager) Find(id int64) IDocument { + if object, exists := manager.cache.Get(id); !exists { + return nil + } else { + return object.(IDocument) + } +} + +func (manager *documentManager) ForEach(iterator DocumentIterator) { + manager.cache.ForEachValue(func(object ISystemObject) { + iterator(object.(IDocument)) + }) +} + +func (manager *documentManager) Filter(predicate DocumentFilterPredicate) IDocumentCollection { + documents := NewDocuments() + if predicate == nil { + return documents + } + + manager.ForEach(func(document IDocument) { + if predicate(document) { + documents.Append(document) + } + }) + + return documents +} + +func (manager *documentManager) Map(predicate DocumentMapPredicate) IDocumentCollection { + documents := NewDocuments() + if predicate == nil { + return documents + } + + manager.ForEach(func(document IDocument) { + documents.Append(predicate(document)) + }) + + return documents +} diff --git a/greataped/components/core/document_manager_test.go b/greataped/components/core/document_manager_test.go new file mode 100644 index 0000000..690ed51 --- /dev/null +++ b/greataped/components/core/document_manager_test.go @@ -0,0 +1,150 @@ +package core_test + +import ( + "testing" + + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/core" +) + +func TestDocumentManager_GetName(test *testing.T) { + manager := Conductor.DocumentManager() + + if manager.Name() != DOCUMENT_MANAGER { + test.Fail() + } +} + +func TestDocumentManager_ResolveDependencies(test *testing.T) { + manager := Conductor.DocumentManager() + + if err := manager.ResolveDependencies(); err != nil { + test.Fatal(err) + } +} + +func TestDocumentManager_Load(test *testing.T) { + manager := Conductor.DocumentManager() + + if err := manager.Load(); err != nil { + test.Fatal(err) + } +} + +func TestDocumentManager_Reload(test *testing.T) { + manager := Conductor.DocumentManager() + + if err := manager.Reload(); err != nil && err != ERROR_OPERATION_NOT_SUPPORTED { + test.Fatal(err) + } +} + +func TestDocumentManager_Count(test *testing.T) { + manager := Conductor.DocumentManager() + + _ = manager.Count() +} + +func TestDocumentManager_Exists(test *testing.T) { + manager := Conductor.DocumentManager() + + if manager.Exists(0) { + test.FailNow() + } +} + +func TestDocumentManager_ListDocuments(test *testing.T) { + manager := Conductor.DocumentManager() + + _ = manager.ListDocuments(0, 0, "", nil) +} + +func TestDocumentManager_GetDocument(test *testing.T) { + manager := Conductor.DocumentManager() + + if document, err := manager.GetDocument(0, nil); err == nil { + _ = document + test.FailNow() + } +} + +func TestDocumentManager_AddDocument(test *testing.T) { + manager := Conductor.DocumentManager() + + document, err := manager.AddDocument("content", nil) + if err != nil { + test.Fatal(err) + } + + _ = document +} + +func TestDocumentManager_UpdateDocument(test *testing.T) { + manager := Conductor.DocumentManager() + + document, err := manager.UpdateDocument(0, "content", nil) + if err != nil { + test.Fatal(err) + } + + _ = document +} + +func TestDocumentManager_RemoveDocument(test *testing.T) { + manager := Conductor.DocumentManager() + + document, err := manager.RemoveDocument(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = document +} + +func TestDocumentManager_Find(test *testing.T) { + manager := Conductor.DocumentManager() + + document := manager.Find(0) + if document == nil { + test.Fail() + } + + _ = document +} + +func TestDocumentManager_ForEach(test *testing.T) { + manager := Conductor.DocumentManager() + + manager.ForEach(func(document IDocument) { + _ = document + }) +} + +func TestDocumentManager_Filter(test *testing.T) { + manager := Conductor.DocumentManager() + + documents := manager.Filter(func(document IDocument) bool { + return document.Id() < 0 + }) + + if documents.IsNotEmpty() { + test.Fail() + } + + _ = documents +} + +func TestDocumentManager_Map(test *testing.T) { + manager := Conductor.DocumentManager() + + documents := manager.Map(func(document IDocument) IDocument { + return document + }) + + if documents.Count() != manager.Count() { + test.Fail() + } + + _ = documents +} diff --git a/greataped/components/core/factory.go b/greataped/components/core/factory.go new file mode 100644 index 0000000..e198cd5 --- /dev/null +++ b/greataped/components/core/factory.go @@ -0,0 +1,54 @@ +package core + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/settings" + . "rail.town/infrastructure/components/contracts" +) + +type systemComponentFactory struct { + components []ISystemComponent +} + +func (factory *systemComponentFactory) Create(componentType SystemComponentType, configuration IConfiguration, logger ILogger, dependencies ...ISystemComponent) ISystemComponent { + var component ISystemComponent = nil + + switch componentType { + case SYSTEM_COMPONENT_DOCUMENT_MANAGER: + component = newDocumentManager(configuration, logger, dependencies...) + case SYSTEM_COMPONENT_SYSTEM_SCHEDULE_MANAGER: + component = newSystemScheduleManager(configuration, logger, dependencies...) + case SYSTEM_COMPONENT_IDENTITY_MANAGER: + component = newIdentityManager(configuration, logger, dependencies...) + case SYSTEM_COMPONENT_ACCESS_CONTROL_MANAGER: + component = newAccessControlManager(configuration, logger, dependencies...) + case SYSTEM_COMPONENT_REMOTE_ACTIVITY_MANAGER: + component = newRemoteActivityManager(configuration, logger, dependencies...) + case SYSTEM_COMPONENT_CATEGORY_TYPE_MANAGER: + component = newCategoryTypeManager(configuration, logger, dependencies...) + case SYSTEM_COMPONENT_CATEGORY_MANAGER: + component = newCategoryManager(configuration, logger, dependencies...) + case SYSTEM_COMPONENT_USER_MANAGER: + component = newUserManager(configuration, logger, dependencies...) + case SYSTEM_COMPONENT_SPI_MANAGER: + component = newSpiManager(configuration, logger, dependencies...) + case SYSTEM_COMPONENT_CUSTOM_ERROR_MANAGER: + component = newCustomErrorManager(configuration, logger, dependencies...) + } + + if component != nil { + factory.components = append(factory.components, component) + } + + return component +} + +func (factory *systemComponentFactory) Components() []ISystemComponent { + return factory.components +} + +func newSystemComponentFactory() ISystemComponentFactory { + return &systemComponentFactory{ + components: []ISystemComponent{}, + } +} diff --git a/greataped/components/core/factory_test.go b/greataped/components/core/factory_test.go new file mode 100644 index 0000000..1f6454a --- /dev/null +++ b/greataped/components/core/factory_test.go @@ -0,0 +1,29 @@ +package core_test + +import ( + "os" + "testing" + + "github.com/xeronith/diamante/logging" + "github.com/xeronith/diamante/settings" + "rail.town/infrastructure/components/core" + "rail.town/infrastructure/components/model/repository" +) + +//region Initialization + +func TestMain(main *testing.M) { + logger := logging.NewLogger(false) + configuration := settings.NewTestConfiguration() + if err := repository.Initialize(configuration, logger); err != nil { + os.Exit(1) + } + + if err := core.Initialize(configuration, logger); err != nil { + os.Exit(1) + } + + os.Exit(main.Run()) +} + +//endregion diff --git a/greataped/components/core/identity.go b/greataped/components/core/identity.go new file mode 100644 index 0000000..16bd424 --- /dev/null +++ b/greataped/components/core/identity.go @@ -0,0 +1,860 @@ +package core + +import ( + "fmt" + + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/system" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + "rail.town/infrastructure/components/model/repository" +) + +type identity struct { + object + username string + phoneNumber string + phoneNumberConfirmed bool + firstName string + lastName string + displayName string + email string + emailConfirmed bool + avatar string + banner string + summary string + token string + multiFactor bool + hash string + salt string + publicKey string + privateKey string + permission uint64 + restriction uint32 + lastLogin int64 + loginCount uint32 + remoteAddress string + userAgent string + systemCallHandler func(Identity, []string) error +} + +// noinspection GoUnusedExportedFunction +func InitializeIdentity() { + _ = ENABLE_SECURITY + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + _ = repository.Initialize +} + +func NewIdentity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) (IIdentity, error) { + instance := &identity{ + object: object{ + id: id, + }, + username: username, + phoneNumber: phoneNumber, + phoneNumberConfirmed: phoneNumberConfirmed, + firstName: firstName, + lastName: lastName, + displayName: displayName, + email: email, + emailConfirmed: emailConfirmed, + avatar: avatar, + banner: banner, + summary: summary, + token: token, + multiFactor: multiFactor, + hash: hash, + salt: salt, + publicKey: publicKey, + privateKey: privateKey, + permission: permission, + restriction: restriction, + lastLogin: lastLogin, + loginCount: loginCount, + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func NewIdentityFromEntity(entity IIdentityEntity) (IIdentity, error) { + instance := &identity{ + object: object{ + id: entity.Id(), + }, + username: entity.Username(), + phoneNumber: entity.PhoneNumber(), + phoneNumberConfirmed: entity.PhoneNumberConfirmed(), + firstName: entity.FirstName(), + lastName: entity.LastName(), + displayName: entity.DisplayName(), + email: entity.Email(), + emailConfirmed: entity.EmailConfirmed(), + avatar: entity.Avatar(), + banner: entity.Banner(), + summary: entity.Summary(), + token: entity.Token(), + multiFactor: entity.MultiFactor(), + hash: entity.Hash(), + salt: entity.Salt(), + publicKey: entity.PublicKey(), + privateKey: entity.PrivateKey(), + permission: entity.Permission(), + restriction: entity.Restriction(), + lastLogin: entity.LastLogin(), + loginCount: entity.LoginCount(), + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func NewAnonymousIdentity(token, remoteAddress string, userAgent string) IIdentity { + return &identity{ + object: object{id: 0}, + username: "anonymous", + phoneNumber: "", + token: token, + remoteAddress: remoteAddress, + userAgent: userAgent, + permission: ANONYMOUS, + restriction: 0, + } +} + +func NewSystemIdentity() IIdentity { + return &identity{ + object: object{id: 1}, + username: "system", + permission: ADMINISTRATOR, + restriction: 0, + } +} + +func (identity *identity) SetSystemCallHandler(handler func(Identity, []string) error) { + identity.systemCallHandler = handler +} + +func (identity *identity) SystemCall(editor Identity, args []string) error { + if identity.systemCallHandler == nil { + return ERROR_INITIALIZE + } + + return identity.systemCallHandler(editor, args) +} + +func (identity *identity) Username() string { + return identity.username +} + +func (identity *identity) UpdateUsername(username string, editor Identity) { + if err := repository.Identities.UpdateUsername(identity.id, username, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.username = username +} + +func (identity *identity) UpdateUsernameAtomic(transaction ITransaction, username string, editor Identity) { + transaction.OnCommit(func() { + identity.username = username + }) + + if err := repository.Identities.UpdateUsernameAtomic(transaction, identity.id, username, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) PhoneNumber() string { + return identity.phoneNumber +} + +func (identity *identity) UpdatePhoneNumber(phoneNumber string, editor Identity) { + if err := repository.Identities.UpdatePhoneNumber(identity.id, phoneNumber, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.phoneNumber = phoneNumber +} + +func (identity *identity) UpdatePhoneNumberAtomic(transaction ITransaction, phoneNumber string, editor Identity) { + transaction.OnCommit(func() { + identity.phoneNumber = phoneNumber + }) + + if err := repository.Identities.UpdatePhoneNumberAtomic(transaction, identity.id, phoneNumber, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) PhoneNumberConfirmed() bool { + return identity.phoneNumberConfirmed +} + +func (identity *identity) UpdatePhoneNumberConfirmed(phoneNumberConfirmed bool, editor Identity) { + if err := repository.Identities.UpdatePhoneNumberConfirmed(identity.id, phoneNumberConfirmed, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.phoneNumberConfirmed = phoneNumberConfirmed +} + +func (identity *identity) UpdatePhoneNumberConfirmedAtomic(transaction ITransaction, phoneNumberConfirmed bool, editor Identity) { + transaction.OnCommit(func() { + identity.phoneNumberConfirmed = phoneNumberConfirmed + }) + + if err := repository.Identities.UpdatePhoneNumberConfirmedAtomic(transaction, identity.id, phoneNumberConfirmed, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) FirstName() string { + return identity.firstName +} + +func (identity *identity) UpdateFirstName(firstName string, editor Identity) { + if err := repository.Identities.UpdateFirstName(identity.id, firstName, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.firstName = firstName +} + +func (identity *identity) UpdateFirstNameAtomic(transaction ITransaction, firstName string, editor Identity) { + transaction.OnCommit(func() { + identity.firstName = firstName + }) + + if err := repository.Identities.UpdateFirstNameAtomic(transaction, identity.id, firstName, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) LastName() string { + return identity.lastName +} + +func (identity *identity) UpdateLastName(lastName string, editor Identity) { + if err := repository.Identities.UpdateLastName(identity.id, lastName, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.lastName = lastName +} + +func (identity *identity) UpdateLastNameAtomic(transaction ITransaction, lastName string, editor Identity) { + transaction.OnCommit(func() { + identity.lastName = lastName + }) + + if err := repository.Identities.UpdateLastNameAtomic(transaction, identity.id, lastName, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) DisplayName() string { + return identity.displayName +} + +func (identity *identity) UpdateDisplayName(displayName string, editor Identity) { + if err := repository.Identities.UpdateDisplayName(identity.id, displayName, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.displayName = displayName +} + +func (identity *identity) UpdateDisplayNameAtomic(transaction ITransaction, displayName string, editor Identity) { + transaction.OnCommit(func() { + identity.displayName = displayName + }) + + if err := repository.Identities.UpdateDisplayNameAtomic(transaction, identity.id, displayName, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) Email() string { + return identity.email +} + +func (identity *identity) UpdateEmail(email string, editor Identity) { + if err := repository.Identities.UpdateEmail(identity.id, email, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.email = email +} + +func (identity *identity) UpdateEmailAtomic(transaction ITransaction, email string, editor Identity) { + transaction.OnCommit(func() { + identity.email = email + }) + + if err := repository.Identities.UpdateEmailAtomic(transaction, identity.id, email, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) EmailConfirmed() bool { + return identity.emailConfirmed +} + +func (identity *identity) UpdateEmailConfirmed(emailConfirmed bool, editor Identity) { + if err := repository.Identities.UpdateEmailConfirmed(identity.id, emailConfirmed, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.emailConfirmed = emailConfirmed +} + +func (identity *identity) UpdateEmailConfirmedAtomic(transaction ITransaction, emailConfirmed bool, editor Identity) { + transaction.OnCommit(func() { + identity.emailConfirmed = emailConfirmed + }) + + if err := repository.Identities.UpdateEmailConfirmedAtomic(transaction, identity.id, emailConfirmed, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) Avatar() string { + return identity.avatar +} + +func (identity *identity) UpdateAvatar(avatar string, editor Identity) { + if err := repository.Identities.UpdateAvatar(identity.id, avatar, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.avatar = avatar +} + +func (identity *identity) UpdateAvatarAtomic(transaction ITransaction, avatar string, editor Identity) { + transaction.OnCommit(func() { + identity.avatar = avatar + }) + + if err := repository.Identities.UpdateAvatarAtomic(transaction, identity.id, avatar, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) Banner() string { + return identity.banner +} + +func (identity *identity) UpdateBanner(banner string, editor Identity) { + if err := repository.Identities.UpdateBanner(identity.id, banner, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.banner = banner +} + +func (identity *identity) UpdateBannerAtomic(transaction ITransaction, banner string, editor Identity) { + transaction.OnCommit(func() { + identity.banner = banner + }) + + if err := repository.Identities.UpdateBannerAtomic(transaction, identity.id, banner, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) Summary() string { + return identity.summary +} + +func (identity *identity) UpdateSummary(summary string, editor Identity) { + if err := repository.Identities.UpdateSummary(identity.id, summary, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.summary = summary +} + +func (identity *identity) UpdateSummaryAtomic(transaction ITransaction, summary string, editor Identity) { + transaction.OnCommit(func() { + identity.summary = summary + }) + + if err := repository.Identities.UpdateSummaryAtomic(transaction, identity.id, summary, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) Token() string { + return identity.token +} + +func (identity *identity) UpdateToken(token string, editor Identity) { + if err := repository.Identities.UpdateToken(identity.id, token, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.token = token +} + +func (identity *identity) UpdateTokenAtomic(transaction ITransaction, token string, editor Identity) { + transaction.OnCommit(func() { + identity.token = token + }) + + if err := repository.Identities.UpdateTokenAtomic(transaction, identity.id, token, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) MultiFactor() bool { + return identity.multiFactor +} + +func (identity *identity) UpdateMultiFactor(multiFactor bool, editor Identity) { + if err := repository.Identities.UpdateMultiFactor(identity.id, multiFactor, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.multiFactor = multiFactor +} + +func (identity *identity) UpdateMultiFactorAtomic(transaction ITransaction, multiFactor bool, editor Identity) { + transaction.OnCommit(func() { + identity.multiFactor = multiFactor + }) + + if err := repository.Identities.UpdateMultiFactorAtomic(transaction, identity.id, multiFactor, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) Hash() string { + return identity.hash +} + +func (identity *identity) UpdateHash(hash string, editor Identity) { + if err := repository.Identities.UpdateHash(identity.id, hash, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.hash = hash +} + +func (identity *identity) UpdateHashAtomic(transaction ITransaction, hash string, editor Identity) { + transaction.OnCommit(func() { + identity.hash = hash + }) + + if err := repository.Identities.UpdateHashAtomic(transaction, identity.id, hash, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) Salt() string { + return identity.salt +} + +func (identity *identity) UpdateSalt(salt string, editor Identity) { + if err := repository.Identities.UpdateSalt(identity.id, salt, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.salt = salt +} + +func (identity *identity) UpdateSaltAtomic(transaction ITransaction, salt string, editor Identity) { + transaction.OnCommit(func() { + identity.salt = salt + }) + + if err := repository.Identities.UpdateSaltAtomic(transaction, identity.id, salt, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) PublicKey() string { + return identity.publicKey +} + +func (identity *identity) UpdatePublicKey(publicKey string, editor Identity) { + if err := repository.Identities.UpdatePublicKey(identity.id, publicKey, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.publicKey = publicKey +} + +func (identity *identity) UpdatePublicKeyAtomic(transaction ITransaction, publicKey string, editor Identity) { + transaction.OnCommit(func() { + identity.publicKey = publicKey + }) + + if err := repository.Identities.UpdatePublicKeyAtomic(transaction, identity.id, publicKey, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) PrivateKey() string { + return identity.privateKey +} + +func (identity *identity) UpdatePrivateKey(privateKey string, editor Identity) { + if err := repository.Identities.UpdatePrivateKey(identity.id, privateKey, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.privateKey = privateKey +} + +func (identity *identity) UpdatePrivateKeyAtomic(transaction ITransaction, privateKey string, editor Identity) { + transaction.OnCommit(func() { + identity.privateKey = privateKey + }) + + if err := repository.Identities.UpdatePrivateKeyAtomic(transaction, identity.id, privateKey, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) Permission() uint64 { + return identity.permission +} + +func (identity *identity) UpdatePermission(permission uint64, editor Identity) { + if err := repository.Identities.UpdatePermission(identity.id, permission, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.permission = permission +} + +func (identity *identity) UpdatePermissionAtomic(transaction ITransaction, permission uint64, editor Identity) { + transaction.OnCommit(func() { + identity.permission = permission + }) + + if err := repository.Identities.UpdatePermissionAtomic(transaction, identity.id, permission, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) Restriction() uint32 { + return identity.restriction +} + +func (identity *identity) UpdateRestriction(restriction uint32, editor Identity) { + if err := repository.Identities.UpdateRestriction(identity.id, restriction, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.restriction = restriction +} + +func (identity *identity) UpdateRestrictionAtomic(transaction ITransaction, restriction uint32, editor Identity) { + transaction.OnCommit(func() { + identity.restriction = restriction + }) + + if err := repository.Identities.UpdateRestrictionAtomic(transaction, identity.id, restriction, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) LastLogin() int64 { + return identity.lastLogin +} + +func (identity *identity) UpdateLastLogin(lastLogin int64, editor Identity) { + if err := repository.Identities.UpdateLastLogin(identity.id, lastLogin, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.lastLogin = lastLogin +} + +func (identity *identity) UpdateLastLoginAtomic(transaction ITransaction, lastLogin int64, editor Identity) { + transaction.OnCommit(func() { + identity.lastLogin = lastLogin + }) + + if err := repository.Identities.UpdateLastLoginAtomic(transaction, identity.id, lastLogin, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) LoginCount() uint32 { + return identity.loginCount +} + +func (identity *identity) UpdateLoginCount(loginCount uint32, editor Identity) { + if err := repository.Identities.UpdateLoginCount(identity.id, loginCount, editor.Id()); err != nil { + panic(err.Error()) + } + + identity.loginCount = loginCount +} + +func (identity *identity) UpdateLoginCountAtomic(transaction ITransaction, loginCount uint32, editor Identity) { + transaction.OnCommit(func() { + identity.loginCount = loginCount + }) + + if err := repository.Identities.UpdateLoginCountAtomic(transaction, identity.id, loginCount, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (identity *identity) RemoteAddress() string { + return identity.remoteAddress +} + +func (identity *identity) SetRemoteAddress(value string) { + identity.remoteAddress = value +} + +func (identity *identity) UserAgent() string { + return identity.userAgent +} + +func (identity *identity) SetUserAgent(value string) { + identity.userAgent = value +} + +func (identity *identity) Role() Role { + return identity.permission +} + +func (identity *identity) IsInRole(role Role) bool { + identityRole := identity.permission & role + return identityRole == role +} + +func (identity *identity) IsRestricted() bool { + return identity.restriction != 0 +} + +func (identity *identity) IsNotRestricted() bool { + return identity.restriction == 0 +} + +func (identity *identity) Payload() Pointer { + return identity +} + +func (identity *identity) SetToken(token string) { + identity.token = token +} + +func (identity *identity) Validate() error { + return nil +} + +func (identity *identity) String() string { + return fmt.Sprintf("Identity (Id: %d, Username: %v, PhoneNumber: %v, PhoneNumberConfirmed: %v, FirstName: %v, LastName: %v, DisplayName: %v, Email: %v, EmailConfirmed: %v, Avatar: %v, Banner: %v, Summary: %v, Token: %v, MultiFactor: %v, Hash: %v, Salt: %v, PublicKey: %v, PrivateKey: %v, Permission: %v, Restriction: %v, LastLogin: %v, LoginCount: %v)", identity.Id(), identity.Username(), identity.PhoneNumber(), identity.PhoneNumberConfirmed(), identity.FirstName(), identity.LastName(), identity.DisplayName(), identity.Email(), identity.EmailConfirmed(), identity.Avatar(), identity.Banner(), identity.Summary(), identity.Token(), identity.MultiFactor(), identity.Hash(), identity.Salt(), identity.PublicKey(), identity.PrivateKey(), identity.Permission(), identity.Restriction(), identity.LastLogin(), identity.LoginCount()) +} + +//------------------------------------------------------------------------------ + +type identities struct { + collection Identities +} + +// NewIdentities creates an empty collection of 'Identity' which is not thread-safe. +func NewIdentities() IIdentityCollection { + return &identities{ + collection: make(Identities, 0), + } +} + +func (identities *identities) Count() int { + return len(identities.collection) +} + +func (identities *identities) IsEmpty() bool { + return len(identities.collection) == 0 +} + +func (identities *identities) IsNotEmpty() bool { + return len(identities.collection) > 0 +} + +func (identities *identities) HasExactlyOneItem() bool { + return len(identities.collection) == 1 +} + +func (identities *identities) HasAtLeastOneItem() bool { + return len(identities.collection) >= 1 +} + +func (identities *identities) First() IIdentity { + return identities.collection[0] +} + +func (identities *identities) Append(identity IIdentity) { + identities.collection = append(identities.collection, identity) +} + +func (identities *identities) ForEach(iterator IdentityIterator) { + if iterator == nil { + return + } + + for _, value := range identities.collection { + iterator(value) + } +} + +func (identities *identities) Array() Identities { + return identities.collection +} + +//------------------------------------------------------------------------------ + +func (dispatcher *dispatcher) IdentityExists(id int64) bool { + return dispatcher.conductor.IdentityManager().Exists(id) +} + +func (dispatcher *dispatcher) IdentityExistsWhich(condition IdentityCondition) bool { + return dispatcher.conductor.IdentityManager().ExistsWhich(condition) +} + +func (dispatcher *dispatcher) ListIdentities() IIdentityCollection { + return dispatcher.conductor.IdentityManager().ListIdentities(0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachIdentity(iterator IdentityIterator) { + dispatcher.conductor.IdentityManager().ForEach(iterator) +} + +func (dispatcher *dispatcher) FilterIdentities(predicate IdentityFilterPredicate) IIdentityCollection { + return dispatcher.conductor.IdentityManager().Filter(predicate) +} + +func (dispatcher *dispatcher) MapIdentities(predicate IdentityMapPredicate) IIdentityCollection { + return dispatcher.conductor.IdentityManager().Map(predicate) +} + +func (dispatcher *dispatcher) GetIdentity(id int64) IIdentity { + if identity, err := dispatcher.conductor.IdentityManager().GetIdentity(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } +} + +func (dispatcher *dispatcher) AddIdentity(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) IIdentity { + transaction := dispatcher.transaction + if transaction != nil { + if identity, err := dispatcher.conductor.IdentityManager().AddIdentityAtomic(transaction, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } else { + if identity, err := dispatcher.conductor.IdentityManager().AddIdentity(username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } +} + +func (dispatcher *dispatcher) AddIdentityWithCustomId(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) IIdentity { + transaction := dispatcher.transaction + if transaction != nil { + if identity, err := dispatcher.conductor.IdentityManager().AddIdentityWithCustomIdAtomic(id, transaction, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } else { + if identity, err := dispatcher.conductor.IdentityManager().AddIdentityWithCustomId(id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } +} + +func (dispatcher *dispatcher) LogIdentity(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, source string, payload string) { + dispatcher.conductor.IdentityManager().Log(username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, source, dispatcher.identity, payload) +} + +func (dispatcher *dispatcher) UpdateIdentity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) IIdentity { + transaction := dispatcher.transaction + if transaction != nil { + if identity, err := dispatcher.conductor.IdentityManager().UpdateIdentityAtomic(transaction, id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } else { + if identity, err := dispatcher.conductor.IdentityManager().UpdateIdentity(id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } +} + +// noinspection GoUnusedParameter +func (dispatcher *dispatcher) UpdateIdentityObject(object IObject, identity IIdentity) IIdentity { + transaction := dispatcher.transaction + if transaction != nil { + if identity, err := dispatcher.conductor.IdentityManager().UpdateIdentityAtomic(transaction, object.Id(), identity.Username(), identity.PhoneNumber(), identity.PhoneNumberConfirmed(), identity.FirstName(), identity.LastName(), identity.DisplayName(), identity.Email(), identity.EmailConfirmed(), identity.Avatar(), identity.Banner(), identity.Summary(), identity.Token(), identity.MultiFactor(), identity.Hash(), identity.Salt(), identity.PublicKey(), identity.PrivateKey(), identity.Permission(), identity.Restriction(), identity.LastLogin(), identity.LoginCount(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } else { + if identity, err := dispatcher.conductor.IdentityManager().UpdateIdentity(object.Id(), identity.Username(), identity.PhoneNumber(), identity.PhoneNumberConfirmed(), identity.FirstName(), identity.LastName(), identity.DisplayName(), identity.Email(), identity.EmailConfirmed(), identity.Avatar(), identity.Banner(), identity.Summary(), identity.Token(), identity.MultiFactor(), identity.Hash(), identity.Salt(), identity.PublicKey(), identity.PrivateKey(), identity.Permission(), identity.Restriction(), identity.LastLogin(), identity.LoginCount(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } +} + +func (dispatcher *dispatcher) AddOrUpdateIdentityObject(object IObject, identity IIdentity) IIdentity { + transaction := dispatcher.transaction + if transaction != nil { + if identity, err := dispatcher.conductor.IdentityManager().AddOrUpdateIdentityObjectAtomic(transaction, object.Id(), identity, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } else { + if identity, err := dispatcher.conductor.IdentityManager().AddOrUpdateIdentityObject(object.Id(), identity, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } +} + +func (dispatcher *dispatcher) RemoveIdentity(id int64) IIdentity { + transaction := dispatcher.transaction + if transaction != nil { + if identity, err := dispatcher.conductor.IdentityManager().RemoveIdentityAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } else { + if identity, err := dispatcher.conductor.IdentityManager().RemoveIdentity(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return identity + } + } +} diff --git a/greataped/components/core/identity_manager.go b/greataped/components/core/identity_manager.go new file mode 100644 index 0000000..afd63a9 --- /dev/null +++ b/greataped/components/core/identity_manager.go @@ -0,0 +1,367 @@ +package core + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/security" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" + "rail.town/infrastructure/components/model/repository" +) + +// noinspection GoSnakeCaseUsage +const IDENTITY_MANAGER = "IdentityManager" + +type identityManager struct { + systemComponent + cache IdentityCache + accessControlHandler IAccessControlHandler +} + +func newIdentityManager(configuration IConfiguration, logger ILogger, dependencies ...ISystemComponent) IIdentityManager { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + + manager := &identityManager{ + systemComponent: newSystemComponent(configuration, logger), + cache: NewIdentityCache(), + } + + if err := manager.ResolveDependencies(dependencies...); err != nil { + return nil + } + + return manager +} + +func (manager *identityManager) Name() string { + return IDENTITY_MANAGER +} + +func (manager *identityManager) ResolveDependencies(_ ...ISystemComponent) error { + return nil +} + +func (manager *identityManager) Load() error { + identityEntities, err := repository.Identities.FetchAll() + if err != nil { + return err + } + + identities := make(SystemObjectCache) + for _, identityEntity := range identityEntities { + if identity, err := NewIdentityFromEntity(identityEntity); err == nil { + identities[identity.Id()] = identity + } else { + return err + } + } + + manager.cache.Load(identities) + return nil +} + +func (manager *identityManager) Reload() error { + return manager.Load() +} + +func (manager *identityManager) OnCacheChanged(callback IdentityCacheCallback) { + manager.cache.OnChanged(callback) +} + +func (manager *identityManager) Count() int { + return manager.cache.Size() +} + +func (manager *identityManager) Exists(id int64) bool { + return manager.Find(id) != nil +} + +func (manager *identityManager) ExistsWhich(condition IdentityCondition) bool { + var identities Identities + manager.ForEach(func(identity IIdentity) { + if condition(identity) { + identities = append(identities, identity) + } + }) + + return len(identities) > 0 +} + +func (manager *identityManager) ListIdentities(_ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) IIdentityCollection { + return manager.Filter(IdentityPassThroughFilter) +} + +func (manager *identityManager) GetIdentity(id int64, _ Identity) (IIdentity, error) { + if identity := manager.Find(id); identity == nil { + return nil, ERROR_IDENTITY_NOT_FOUND + } else { + return identity, nil + } +} + +func (manager *identityManager) AddIdentity(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) { + identityEntity := NewIdentityEntity(manager.UniqueId(), username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount) + return manager.Apply(identityEntity, repository.Identities.Add, manager.cache.Put, editor) +} + +func (manager *identityManager) AddIdentityWithCustomId(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) { + identityEntity := NewIdentityEntity(id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount) + return manager.Apply(identityEntity, repository.Identities.Add, manager.cache.Put, editor) +} + +func (manager *identityManager) AddIdentityObject(identity IIdentity, editor Identity) (IIdentity, error) { + identityEntity := NewIdentityEntity(manager.UniqueId(), identity.Username(), identity.PhoneNumber(), identity.PhoneNumberConfirmed(), identity.FirstName(), identity.LastName(), identity.DisplayName(), identity.Email(), identity.EmailConfirmed(), identity.Avatar(), identity.Banner(), identity.Summary(), identity.Token(), identity.MultiFactor(), identity.Hash(), identity.Salt(), identity.PublicKey(), identity.PrivateKey(), identity.Permission(), identity.Restriction(), identity.LastLogin(), identity.LoginCount()) + return manager.Apply(identityEntity, repository.Identities.Add, manager.cache.Put, editor) +} + +func (manager *identityManager) AddIdentityAtomic(transaction ITransaction, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) { + identityEntity := NewIdentityEntity(manager.UniqueId(), username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount) + return manager.ApplyAtomic(transaction, identityEntity, repository.Identities.AddAtomic, manager.cache.Put, editor) +} + +func (manager *identityManager) AddIdentityWithCustomIdAtomic(id int64, transaction ITransaction, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) { + identityEntity := NewIdentityEntity(id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount) + return manager.ApplyAtomic(transaction, identityEntity, repository.Identities.AddAtomic, manager.cache.Put, editor) +} + +func (manager *identityManager) AddIdentityObjectAtomic(transaction ITransaction, identity IIdentity, editor Identity) (IIdentity, error) { + identityEntity := NewIdentityEntity(manager.UniqueId(), identity.Username(), identity.PhoneNumber(), identity.PhoneNumberConfirmed(), identity.FirstName(), identity.LastName(), identity.DisplayName(), identity.Email(), identity.EmailConfirmed(), identity.Avatar(), identity.Banner(), identity.Summary(), identity.Token(), identity.MultiFactor(), identity.Hash(), identity.Salt(), identity.PublicKey(), identity.PrivateKey(), identity.Permission(), identity.Restriction(), identity.LastLogin(), identity.LoginCount()) + return manager.ApplyAtomic(transaction, identityEntity, repository.Identities.AddAtomic, manager.cache.Put, editor) +} + +func (manager *identityManager) Log(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, source string, editor Identity, payload string) { + identityPipeEntity := NewIdentityPipeEntity(manager.UniqueId(), username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, source, editor.Id(), payload) + repository.Pipe.Insert(identityPipeEntity) + + identity, err := NewIdentityFromEntity(identityPipeEntity) + if err != nil { + manager.Logger().Error(err) + } else { + manager.cache.Put(identity.Id(), identity) + } +} + +func (manager *identityManager) UpdateIdentity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) { + identityEntity := NewIdentityEntity(id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount) + return manager.Apply(identityEntity, repository.Identities.Update, manager.cache.Put, editor) +} + +func (manager *identityManager) UpdateIdentityObject(id int64, identity IIdentity, editor Identity) (IIdentity, error) { + identityEntity := NewIdentityEntity(id, identity.Username(), identity.PhoneNumber(), identity.PhoneNumberConfirmed(), identity.FirstName(), identity.LastName(), identity.DisplayName(), identity.Email(), identity.EmailConfirmed(), identity.Avatar(), identity.Banner(), identity.Summary(), identity.Token(), identity.MultiFactor(), identity.Hash(), identity.Salt(), identity.PublicKey(), identity.PrivateKey(), identity.Permission(), identity.Restriction(), identity.LastLogin(), identity.LoginCount()) + return manager.Apply(identityEntity, repository.Identities.Update, manager.cache.Put, editor) +} + +func (manager *identityManager) UpdateIdentityAtomic(transaction ITransaction, id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) { + identityEntity := NewIdentityEntity(id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount) + return manager.ApplyAtomic(transaction, identityEntity, repository.Identities.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *identityManager) UpdateIdentityObjectAtomic(transaction ITransaction, id int64, identity IIdentity, editor Identity) (IIdentity, error) { + identityEntity := NewIdentityEntity(id, identity.Username(), identity.PhoneNumber(), identity.PhoneNumberConfirmed(), identity.FirstName(), identity.LastName(), identity.DisplayName(), identity.Email(), identity.EmailConfirmed(), identity.Avatar(), identity.Banner(), identity.Summary(), identity.Token(), identity.MultiFactor(), identity.Hash(), identity.Salt(), identity.PublicKey(), identity.PrivateKey(), identity.Permission(), identity.Restriction(), identity.LastLogin(), identity.LoginCount()) + return manager.ApplyAtomic(transaction, identityEntity, repository.Identities.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *identityManager) AddOrUpdateIdentityObject(id int64, identity IIdentity, editor Identity) (IIdentity, error) { + if manager.Exists(id) { + return manager.UpdateIdentityObject(id, identity, editor) + } else { + return manager.AddIdentityObject(identity, editor) + } +} + +func (manager *identityManager) AddOrUpdateIdentityObjectAtomic(transaction ITransaction, id int64, identity IIdentity, editor Identity) (IIdentity, error) { + if manager.Exists(id) { + return manager.UpdateIdentityObjectAtomic(transaction, id, identity, editor) + } else { + return manager.AddIdentityObjectAtomic(transaction, identity, editor) + } +} + +func (manager *identityManager) RemoveIdentity(_ int64, _ Identity) (IIdentity, error) { + return nil, ERROR_OPERATION_NOT_SUPPORTED +} + +func (manager *identityManager) RemoveIdentityAtomic(_ ITransaction, _ int64, _ Identity) (IIdentity, error) { + return nil, ERROR_OPERATION_NOT_SUPPORTED +} + +func (manager *identityManager) Apply(identityEntity IIdentityEntity, repositoryHandler func(IIdentityEntity, int64) error, cacheHandler func(int64, Identity), editor Identity) (IIdentity, error) { + result, err := NewIdentityFromEntity(identityEntity) + if err != nil { + return nil, err + } + + if err := repositoryHandler(identityEntity, editor.Id()); err != nil { + return nil, err + } + + cacheHandler(result.Id(), result) + return result, nil +} + +func (manager *identityManager) ApplyAtomic(transaction ITransaction, identityEntity IIdentityEntity, repositoryHandler func(IRepositoryTransaction, IIdentityEntity, int64) error, cacheHandler func(int64, Identity), editor Identity) (IIdentity, error) { + result, err := NewIdentityFromEntity(identityEntity) + if err != nil { + return nil, err + } + + transaction.OnCommit(func() { + cacheHandler(result.Id(), result) + }) + + if err := repositoryHandler(transaction, identityEntity, editor.Id()); err != nil { + return nil, err + } + + return result, nil +} + +func (manager *identityManager) Find(id int64) IIdentity { + if object, exists := manager.cache.Get(id); !exists { + return nil + } else { + return object.(IIdentity) + } +} + +func (manager *identityManager) ForEach(iterator IdentityIterator) { + manager.cache.ForEachValue(func(object ISystemObject) { + iterator(object.(IIdentity)) + }) +} + +func (manager *identityManager) Filter(predicate IdentityFilterPredicate) IIdentityCollection { + identities := NewIdentities() + if predicate == nil { + return identities + } + + manager.ForEach(func(identity IIdentity) { + if predicate(identity) { + identities.Append(identity) + } + }) + + return identities +} + +func (manager *identityManager) Map(predicate IdentityMapPredicate) IIdentityCollection { + identities := NewIdentities() + if predicate == nil { + return identities + } + + manager.ForEach(func(identity IIdentity) { + identities.Append(predicate(identity)) + }) + + return identities +} + +//region ISecurityHandler Implementation + +func (manager *identityManager) AccessControlHandler() IAccessControlHandler { + return manager.accessControlHandler +} + +func (manager *identityManager) SetAccessControlHandler(handler IAccessControlHandler) { + manager.accessControlHandler = handler +} + +func (manager *identityManager) Validate(phoneNumber string, password string) (string, error) { + identity, exists := manager.cache.GetByPhoneNumber(phoneNumber) + if !exists { + return "", ERROR_USER_NOT_REGISTERED + } + + if identity.IsRestricted() { + return "", ERROR_ACCOUNT_BLOCKED + } + + if identity.MultiFactor() && manager.GenerateHash(password, identity.Salt()) != identity.Hash() { + return "", ERROR_INVALID_CREDENTIALS + } + + confirmationCode := manager.GenerateCode() + if manager.IsStagingEnvironment() || identity.MultiFactor() { + confirmationCode = "123456" + } + + token := manager.GenerateHash(phoneNumber, confirmationCode) + manager.cache.StoreAuthorizationInfo(token, phoneNumber, confirmationCode) + if manager.IsProductionEnvironment() && !identity.MultiFactor() { + manager.SMS(phoneNumber, "Confirmation Code: %s", confirmationCode) + } + + return token, nil +} + +func (manager *identityManager) Verify(token string, confirmationCode string) (string, uint64, error) { + storedPhoneNumber, storedConfirmationCode, err := manager.cache.RetrieveAuthorizationInfo(token) + if err != nil { + return "", 0, ERROR_INVALID_TOKEN + } + + if confirmationCode != storedConfirmationCode { + return "", 0, ERROR_INVALID_CONFIRMATION_CODE + } + + identity, exists := manager.cache.GetByPhoneNumber(storedPhoneNumber) + if !exists { + return "", 0, ERROR_USER_NOT_REGISTERED + } + + if _, knownUser := identity.(IIdentity); !knownUser { + return "", 0, ERROR_UNKNOWN_USER + } + + newToken := manager.GenerateHash(storedPhoneNumber, manager.GenerateCode()) + identity.(IIdentity).UpdateLastLogin(manager.UnixNano(), identity) + identity.(IIdentity).UpdateLoginCount(identity.(IIdentity).LoginCount()+1, identity) + if err := manager.UpdateToken(identity, newToken); err != nil { + return "", 0, err + } + + return newToken, identity.Permission(), nil +} + +func (manager *identityManager) RefreshTokenCache(identity Identity, token string) error { + manager.cache.RefreshToken(identity, token) + return nil +} + +func (manager *identityManager) Authenticate(token string, role Role, remoteAddress string, userAgent string) Identity { + switch role { + case ANONYMOUS: + return NewAnonymousIdentity(token, remoteAddress, userAgent) + default: + if identity, exists := manager.cache.GetByToken(token); exists && identity.IsInRole(role) && identity.IsNotRestricted() { + identity.SetRemoteAddress(remoteAddress) + identity.SetUserAgent(userAgent) + return identity + } + } + + return nil +} + +func (manager *identityManager) SignOut(identity Identity) error { + return manager.UpdateToken(identity, manager.GenerateUUID()) +} + +func (manager *identityManager) UpdateToken(identity Identity, token string) error { + if err := repository.Identities.UpdateToken(identity.Id(), token, identity.Id()); err != nil { + return err + } + + manager.cache.RefreshToken(identity, token) + return nil +} + +//endregion diff --git a/greataped/components/core/identity_manager_test.go b/greataped/components/core/identity_manager_test.go new file mode 100644 index 0000000..176381f --- /dev/null +++ b/greataped/components/core/identity_manager_test.go @@ -0,0 +1,150 @@ +package core_test + +import ( + "testing" + + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/core" +) + +func TestIdentityManager_GetName(test *testing.T) { + manager := Conductor.IdentityManager() + + if manager.Name() != IDENTITY_MANAGER { + test.Fail() + } +} + +func TestIdentityManager_ResolveDependencies(test *testing.T) { + manager := Conductor.IdentityManager() + + if err := manager.ResolveDependencies(); err != nil { + test.Fatal(err) + } +} + +func TestIdentityManager_Load(test *testing.T) { + manager := Conductor.IdentityManager() + + if err := manager.Load(); err != nil { + test.Fatal(err) + } +} + +func TestIdentityManager_Reload(test *testing.T) { + manager := Conductor.IdentityManager() + + if err := manager.Reload(); err != nil && err != ERROR_OPERATION_NOT_SUPPORTED { + test.Fatal(err) + } +} + +func TestIdentityManager_Count(test *testing.T) { + manager := Conductor.IdentityManager() + + _ = manager.Count() +} + +func TestIdentityManager_Exists(test *testing.T) { + manager := Conductor.IdentityManager() + + if manager.Exists(0) { + test.FailNow() + } +} + +func TestIdentityManager_ListIdentities(test *testing.T) { + manager := Conductor.IdentityManager() + + _ = manager.ListIdentities(0, 0, "", nil) +} + +func TestIdentityManager_GetIdentity(test *testing.T) { + manager := Conductor.IdentityManager() + + if identity, err := manager.GetIdentity(0, nil); err == nil { + _ = identity + test.FailNow() + } +} + +func TestIdentityManager_AddIdentity(test *testing.T) { + manager := Conductor.IdentityManager() + + identity, err := manager.AddIdentity("username", "phone_number", true, "first_name", "last_name", "display_name", "email", true, "avatar", "banner", "summary", "token", true, "hash", "salt", "public_key", "private_key", 0, 0, 0, 0, nil) + if err != nil { + test.Fatal(err) + } + + _ = identity +} + +func TestIdentityManager_UpdateIdentity(test *testing.T) { + manager := Conductor.IdentityManager() + + identity, err := manager.UpdateIdentity(0, "username", "phone_number", true, "first_name", "last_name", "display_name", "email", true, "avatar", "banner", "summary", "token", true, "hash", "salt", "public_key", "private_key", 0, 0, 0, 0, nil) + if err != nil { + test.Fatal(err) + } + + _ = identity +} + +func TestIdentityManager_RemoveIdentity(test *testing.T) { + manager := Conductor.IdentityManager() + + identity, err := manager.RemoveIdentity(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = identity +} + +func TestIdentityManager_Find(test *testing.T) { + manager := Conductor.IdentityManager() + + identity := manager.Find(0) + if identity == nil { + test.Fail() + } + + _ = identity +} + +func TestIdentityManager_ForEach(test *testing.T) { + manager := Conductor.IdentityManager() + + manager.ForEach(func(identity IIdentity) { + _ = identity + }) +} + +func TestIdentityManager_Filter(test *testing.T) { + manager := Conductor.IdentityManager() + + identities := manager.Filter(func(identity IIdentity) bool { + return identity.Id() < 0 + }) + + if identities.IsNotEmpty() { + test.Fail() + } + + _ = identities +} + +func TestIdentityManager_Map(test *testing.T) { + manager := Conductor.IdentityManager() + + identities := manager.Map(func(identity IIdentity) IIdentity { + return identity + }) + + if identities.Count() != manager.Count() { + test.Fail() + } + + _ = identities +} diff --git a/greataped/components/core/initializer.go b/greataped/components/core/initializer.go new file mode 100644 index 0000000..4bfc50e --- /dev/null +++ b/greataped/components/core/initializer.go @@ -0,0 +1,861 @@ +package core + +import ( + "bytes" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "runtime" + "strconv" + "strings" + "time" + + schedule "github.com/robfig/cron" + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/service" + . "github.com/xeronith/diamante/contracts/settings" + "github.com/xeronith/diamante/utility/httpsig" + app "rail.town/infrastructure/app" + . "rail.town/infrastructure/components/contracts" + "rail.town/infrastructure/components/contracts/model" + "rail.town/infrastructure/components/model/repository" +) + +//goland:noinspection GoUnusedGlobalVariable +var ( + // @formatter:off + Conductor IConductor + componentsContainer SystemComponentsContainer + buildNumber = "0" + runningInContainer string + Dockerized = runningInContainer == "true" + // @formatter:on +) + +func Initialize(configuration IConfiguration, logger ILogger) error { + logger.SysComp("┄ Booting up ...") + + environment := configuration.GetEnvironment() + + if Dockerized { + environment += " (Docker)" + } else { + environment += " (Metal)" + } + + logger.SysComp(fmt.Sprintf("┄ Environment: %s", environment)) + logger.SysComp("┄ Initializing system components ...") + + factory := newSystemComponentFactory() + + // Initializing System Components + documentManager := factory.Create(SYSTEM_COMPONENT_DOCUMENT_MANAGER, configuration, logger).(IDocumentManager) + systemScheduleManager := factory.Create(SYSTEM_COMPONENT_SYSTEM_SCHEDULE_MANAGER, configuration, logger).(ISystemScheduleManager) + identityManager := factory.Create(SYSTEM_COMPONENT_IDENTITY_MANAGER, configuration, logger).(IIdentityManager) + accessControlManager := factory.Create(SYSTEM_COMPONENT_ACCESS_CONTROL_MANAGER, configuration, logger).(IAccessControlManager) + remoteActivityManager := factory.Create(SYSTEM_COMPONENT_REMOTE_ACTIVITY_MANAGER, configuration, logger).(IRemoteActivityManager) + categoryTypeManager := factory.Create(SYSTEM_COMPONENT_CATEGORY_TYPE_MANAGER, configuration, logger).(ICategoryTypeManager) + categoryManager := factory.Create(SYSTEM_COMPONENT_CATEGORY_MANAGER, configuration, logger).(ICategoryManager) + userManager := factory.Create(SYSTEM_COMPONENT_USER_MANAGER, configuration, logger).(IUserManager) + spiManager := factory.Create(SYSTEM_COMPONENT_SPI_MANAGER, configuration, logger).(ISpiManager) + customErrorManager := factory.Create(SYSTEM_COMPONENT_CUSTOM_ERROR_MANAGER, configuration, logger).(ICustomErrorManager) + + // Resolving Dependencies + // @formatter:off + if err := categoryManager.ResolveDependencies(nil, categoryTypeManager, categoryManager); err != nil { + return err + } + // @formatter:on + + identityManager.SetAccessControlHandler(accessControlManager) + location, err := time.LoadLocation("UTC") + if err != nil { + panic(err) + } + + scheduler := schedule.NewWithLocation(location) + scheduler.Start() + + // Aggregating System Components + Conductor = &conductor{ + // @formatter:off + documentManager: documentManager, + systemScheduleManager: systemScheduleManager, + identityManager: identityManager, + accessControlManager: accessControlManager, + remoteActivityManager: remoteActivityManager, + categoryTypeManager: categoryTypeManager, + categoryManager: categoryManager, + userManager: userManager, + spiManager: spiManager, + customErrorManager: customErrorManager, + logger: logger, + configuration: configuration, + scheduler: scheduler, + httpClient: &http.Client{ + Timeout: time.Second * 5, + }, + // @formatter:on + } + + logger.SysComp("┄ Loading system components ...") + + var totalDuration float64 = 0 + componentsContainer = make(SystemComponentsContainer) + for _, component := range factory.Components() { + start := time.Now() + componentName := component.Name() + if _, exists := componentsContainer[componentName]; exists { + return errors.New(fmt.Sprintf("%s already registered", componentName)) + } + + if err := component.Load(); err != nil { + return err + } + + componentsContainer[componentName] = component + duration := time.Since(start).Seconds() + totalDuration += duration + logger.SysComp(fmt.Sprintf("✓ %s: %.2fs", componentName, duration)) + } + + serverBuildNumber, err := strconv.ParseInt(buildNumber, 10, 32) + if err != nil { + return err + } + + configuration.GetServerConfiguration().SetBuildNumber(int32(serverBuildNumber)) + logger.SysComp(fmt.Sprintf("┄ All system components loaded in %.2fs", totalDuration)) + logger.SysComp(fmt.Sprintf("┄ Runtime: %s/%s %s build %s", runtime.GOOS, runtime.GOARCH, runtime.Version(), buildNumber)) + if err := app.Initialize(NewDispatcher(Conductor, NewSystemIdentity())); err != nil { + return err + } + + logger.SysComp("┄ System operational") + + return nil +} + +//region IConductor Implementation + +type conductor struct { + // @formatter:off + documentManager IDocumentManager + systemScheduleManager ISystemScheduleManager + identityManager IIdentityManager + accessControlManager IAccessControlManager + remoteActivityManager IRemoteActivityManager + categoryTypeManager ICategoryTypeManager + categoryManager ICategoryManager + userManager IUserManager + spiManager ISpiManager + customErrorManager ICustomErrorManager + logger ILogger + configuration IConfiguration + scheduler *schedule.Cron + httpClient *http.Client + // @formatter:on +} + +func (conductor *conductor) Logger() ILogger { + return conductor.logger +} + +func (conductor *conductor) Configuration() IConfiguration { + return conductor.configuration +} + +func (conductor *conductor) Atomic(handler TransactionHandler) error { + return repository.WithTransaction(func(transaction model.IRepositoryTransaction) error { + return handler(transaction) + }) +} + +func (conductor *conductor) Schedule(spec string, callback func()) error { + return conductor.scheduler.AddFunc(spec, callback) +} + +func (conductor *conductor) GetSystemComponent(name string) ISystemComponent { + if component, exists := componentsContainer[name]; exists { + return component + } else { + return nil + } +} + +func (conductor *conductor) SignRequest(keyId, privateKey string, data []byte, req *http.Request) error { + privKey, err := httpsig.ParseRsaPrivateKeyFromPemStr(privateKey) + if err != nil { + return err + } + + signer := httpsig.NewRSASHA256Signer(keyId, privKey, []string{"Date", "Digest"}) + if data != nil { + hasher := sha256.New() + hasher.Write(data) + sum := hasher.Sum(nil) + encodedHash := base64.StdEncoding.EncodeToString(sum) + digest := fmt.Sprintf("sha-256=%s", encodedHash) + req.Header.Set("Content-Type", "application/activity+json; charset=utf-8") + req.Header.Set("Digest", digest) + } + + if err := signer.Sign(req); err != nil { + return err + } + + return nil +} + +func (conductor *conductor) RequestActivityStream(method, url, keyId, privateKey string, data []byte, output interface{}) error { + var reader io.Reader + if data != nil { + reader = bytes.NewBuffer(data) + } + + req, err := http.NewRequest(method, url, reader) + if err != nil { + return err + } + + req.Header.Set("Accept", "application/activity+json") + + if privateKey != "" { + if err := conductor.SignRequest(keyId, privateKey, data, req); err != nil { + return err + } + } + + res, err := conductor.httpClient.Do(req) + if err != nil { + return err + } + + if res.Body != nil { + defer res.Body.Close() + } + + if res.StatusCode != http.StatusOK && + res.StatusCode != http.StatusAccepted { + return fmt.Errorf("%s", res.Status) + } + + if output != nil { + if err := json.NewDecoder(res.Body).Decode(output); err != nil { + return err + } + } + + return nil +} + +// Document + +func (conductor *conductor) DocumentManager() IDocumentManager { + return conductor.documentManager +} + +func (conductor *conductor) DocumentExists(id int64) bool { + return conductor.documentManager.Exists(id) +} + +func (conductor *conductor) ListDocuments(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IDocumentCollection { + return conductor.documentManager.ListDocuments(pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) GetDocument(id int64, editor Identity) (IDocument, error) { + return conductor.documentManager.GetDocument(id, editor) +} + +func (conductor *conductor) AddDocument(content string, editor Identity) (IDocument, error) { + return conductor.documentManager.AddDocument(content, editor) +} + +func (conductor *conductor) AddDocumentAtomic(transaction ITransaction, content string, editor Identity) (IDocument, error) { + return conductor.documentManager.AddDocumentAtomic(transaction, content, editor) +} + +func (conductor *conductor) LogDocument(content string, source string, editor Identity, payload string) { + conductor.documentManager.Log(content, source, editor, payload) +} + +func (conductor *conductor) UpdateDocument(id int64, content string, editor Identity) (IDocument, error) { + return conductor.documentManager.UpdateDocument(id, content, editor) +} + +func (conductor *conductor) UpdateDocumentAtomic(transaction ITransaction, id int64, content string, editor Identity) (IDocument, error) { + return conductor.documentManager.UpdateDocumentAtomic(transaction, id, content, editor) +} + +func (conductor *conductor) RemoveDocument(id int64, editor Identity) (IDocument, error) { + return conductor.documentManager.RemoveDocument(id, editor) +} + +func (conductor *conductor) RemoveDocumentAtomic(transaction ITransaction, id int64, editor Identity) (IDocument, error) { + return conductor.documentManager.RemoveDocumentAtomic(transaction, id, editor) +} + +// SystemSchedule + +func (conductor *conductor) SystemScheduleManager() ISystemScheduleManager { + return conductor.systemScheduleManager +} + +func (conductor *conductor) SystemScheduleExists(id int64) bool { + return conductor.systemScheduleManager.Exists(id) +} + +func (conductor *conductor) ListSystemSchedules(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ISystemScheduleCollection { + return conductor.systemScheduleManager.ListSystemSchedules(pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) GetSystemSchedule(id int64, editor Identity) (ISystemSchedule, error) { + return conductor.systemScheduleManager.GetSystemSchedule(id, editor) +} + +func (conductor *conductor) AddSystemSchedule(enabled bool, config string, editor Identity) (ISystemSchedule, error) { + return conductor.systemScheduleManager.AddSystemSchedule(enabled, config, editor) +} + +func (conductor *conductor) AddSystemScheduleAtomic(transaction ITransaction, enabled bool, config string, editor Identity) (ISystemSchedule, error) { + return conductor.systemScheduleManager.AddSystemScheduleAtomic(transaction, enabled, config, editor) +} + +func (conductor *conductor) LogSystemSchedule(enabled bool, config string, source string, editor Identity, payload string) { + conductor.systemScheduleManager.Log(enabled, config, source, editor, payload) +} + +func (conductor *conductor) UpdateSystemSchedule(id int64, enabled bool, config string, editor Identity) (ISystemSchedule, error) { + return conductor.systemScheduleManager.UpdateSystemSchedule(id, enabled, config, editor) +} + +func (conductor *conductor) UpdateSystemScheduleAtomic(transaction ITransaction, id int64, enabled bool, config string, editor Identity) (ISystemSchedule, error) { + return conductor.systemScheduleManager.UpdateSystemScheduleAtomic(transaction, id, enabled, config, editor) +} + +func (conductor *conductor) RemoveSystemSchedule(id int64, editor Identity) (ISystemSchedule, error) { + return conductor.systemScheduleManager.RemoveSystemSchedule(id, editor) +} + +func (conductor *conductor) RemoveSystemScheduleAtomic(transaction ITransaction, id int64, editor Identity) (ISystemSchedule, error) { + return conductor.systemScheduleManager.RemoveSystemScheduleAtomic(transaction, id, editor) +} + +// Identity + +func (conductor *conductor) IdentityManager() IIdentityManager { + return conductor.identityManager +} + +func (conductor *conductor) IdentityExists(id int64) bool { + return conductor.identityManager.Exists(id) +} + +func (conductor *conductor) ListIdentities(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IIdentityCollection { + return conductor.identityManager.ListIdentities(pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) GetIdentity(id int64, editor Identity) (IIdentity, error) { + return conductor.identityManager.GetIdentity(id, editor) +} + +func (conductor *conductor) AddIdentity(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) { + return conductor.identityManager.AddIdentity(username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, editor) +} + +func (conductor *conductor) AddIdentityAtomic(transaction ITransaction, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) { + return conductor.identityManager.AddIdentityAtomic(transaction, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, editor) +} + +func (conductor *conductor) LogIdentity(username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, source string, editor Identity, payload string) { + conductor.identityManager.Log(username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, source, editor, payload) +} + +func (conductor *conductor) UpdateIdentity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) { + return conductor.identityManager.UpdateIdentity(id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, editor) +} + +func (conductor *conductor) UpdateIdentityAtomic(transaction ITransaction, id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, editor Identity) (IIdentity, error) { + return conductor.identityManager.UpdateIdentityAtomic(transaction, id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount, editor) +} + +func (conductor *conductor) RemoveIdentity(id int64, editor Identity) (IIdentity, error) { + return conductor.identityManager.RemoveIdentity(id, editor) +} + +func (conductor *conductor) RemoveIdentityAtomic(transaction ITransaction, id int64, editor Identity) (IIdentity, error) { + return conductor.identityManager.RemoveIdentityAtomic(transaction, id, editor) +} + +// AccessControl + +func (conductor *conductor) AccessControlManager() IAccessControlManager { + return conductor.accessControlManager +} + +func (conductor *conductor) AccessControlExists(id int64) bool { + return conductor.accessControlManager.Exists(id) +} + +func (conductor *conductor) ListAccessControls(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IAccessControlCollection { + return conductor.accessControlManager.ListAccessControls(pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) GetAccessControl(id int64, editor Identity) (IAccessControl, error) { + return conductor.accessControlManager.GetAccessControl(id, editor) +} + +func (conductor *conductor) AddAccessControl(key uint64, value uint64, editor Identity) (IAccessControl, error) { + return conductor.accessControlManager.AddAccessControl(key, value, editor) +} + +func (conductor *conductor) AddAccessControlAtomic(transaction ITransaction, key uint64, value uint64, editor Identity) (IAccessControl, error) { + return conductor.accessControlManager.AddAccessControlAtomic(transaction, key, value, editor) +} + +func (conductor *conductor) LogAccessControl(key uint64, value uint64, source string, editor Identity, payload string) { + conductor.accessControlManager.Log(key, value, source, editor, payload) +} + +func (conductor *conductor) UpdateAccessControl(id int64, key uint64, value uint64, editor Identity) (IAccessControl, error) { + return conductor.accessControlManager.UpdateAccessControl(id, key, value, editor) +} + +func (conductor *conductor) UpdateAccessControlAtomic(transaction ITransaction, id int64, key uint64, value uint64, editor Identity) (IAccessControl, error) { + return conductor.accessControlManager.UpdateAccessControlAtomic(transaction, id, key, value, editor) +} + +func (conductor *conductor) RemoveAccessControl(id int64, editor Identity) (IAccessControl, error) { + return conductor.accessControlManager.RemoveAccessControl(id, editor) +} + +func (conductor *conductor) RemoveAccessControlAtomic(transaction ITransaction, id int64, editor Identity) (IAccessControl, error) { + return conductor.accessControlManager.RemoveAccessControlAtomic(transaction, id, editor) +} + +// RemoteActivity + +func (conductor *conductor) RemoteActivityManager() IRemoteActivityManager { + return conductor.remoteActivityManager +} + +func (conductor *conductor) RemoteActivityExists(id int64) bool { + return conductor.remoteActivityManager.Exists(id) +} + +func (conductor *conductor) ListRemoteActivities(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IRemoteActivityCollection { + return conductor.remoteActivityManager.ListRemoteActivities(pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) GetRemoteActivity(id int64, editor Identity) (IRemoteActivity, error) { + return conductor.remoteActivityManager.GetRemoteActivity(id, editor) +} + +func (conductor *conductor) AddRemoteActivity(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) { + return conductor.remoteActivityManager.AddRemoteActivity(entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, editor) +} + +func (conductor *conductor) AddRemoteActivityAtomic(transaction ITransaction, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) { + return conductor.remoteActivityManager.AddRemoteActivityAtomic(transaction, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, editor) +} + +func (conductor *conductor) LogRemoteActivity(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, source string, editor Identity, payload string) { + conductor.remoteActivityManager.Log(entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, source, editor, payload) +} + +func (conductor *conductor) UpdateRemoteActivity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) { + return conductor.remoteActivityManager.UpdateRemoteActivity(id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, editor) +} + +func (conductor *conductor) UpdateRemoteActivityAtomic(transaction ITransaction, id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) { + return conductor.remoteActivityManager.UpdateRemoteActivityAtomic(transaction, id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, editor) +} + +func (conductor *conductor) RemoveRemoteActivity(id int64, editor Identity) (IRemoteActivity, error) { + return conductor.remoteActivityManager.RemoveRemoteActivity(id, editor) +} + +func (conductor *conductor) RemoveRemoteActivityAtomic(transaction ITransaction, id int64, editor Identity) (IRemoteActivity, error) { + return conductor.remoteActivityManager.RemoveRemoteActivityAtomic(transaction, id, editor) +} + +// CategoryType + +func (conductor *conductor) CategoryTypeManager() ICategoryTypeManager { + return conductor.categoryTypeManager +} + +func (conductor *conductor) CategoryTypeExists(id int64) bool { + return conductor.categoryTypeManager.Exists(id) +} + +func (conductor *conductor) ListCategoryTypes(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryTypeCollection { + return conductor.categoryTypeManager.ListCategoryTypes(pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) GetCategoryType(id int64, editor Identity) (ICategoryType, error) { + return conductor.categoryTypeManager.GetCategoryType(id, editor) +} + +func (conductor *conductor) AddCategoryType(description string, editor Identity) (ICategoryType, error) { + return conductor.categoryTypeManager.AddCategoryType(description, editor) +} + +func (conductor *conductor) AddCategoryTypeAtomic(transaction ITransaction, description string, editor Identity) (ICategoryType, error) { + return conductor.categoryTypeManager.AddCategoryTypeAtomic(transaction, description, editor) +} + +func (conductor *conductor) LogCategoryType(description string, source string, editor Identity, payload string) { + conductor.categoryTypeManager.Log(description, source, editor, payload) +} + +func (conductor *conductor) UpdateCategoryType(id int64, description string, editor Identity) (ICategoryType, error) { + return conductor.categoryTypeManager.UpdateCategoryType(id, description, editor) +} + +func (conductor *conductor) UpdateCategoryTypeAtomic(transaction ITransaction, id int64, description string, editor Identity) (ICategoryType, error) { + return conductor.categoryTypeManager.UpdateCategoryTypeAtomic(transaction, id, description, editor) +} + +func (conductor *conductor) RemoveCategoryType(id int64, editor Identity) (ICategoryType, error) { + return conductor.categoryTypeManager.RemoveCategoryType(id, editor) +} + +func (conductor *conductor) RemoveCategoryTypeAtomic(transaction ITransaction, id int64, editor Identity) (ICategoryType, error) { + return conductor.categoryTypeManager.RemoveCategoryTypeAtomic(transaction, id, editor) +} + +// Category + +func (conductor *conductor) CategoryManager() ICategoryManager { + return conductor.categoryManager +} + +func (conductor *conductor) CategoryExists(id int64) bool { + return conductor.categoryManager.Exists(id) +} + +func (conductor *conductor) ListCategories(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryCollection { + return conductor.categoryManager.ListCategories(pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) GetCategory(id int64, editor Identity) (ICategory, error) { + return conductor.categoryManager.GetCategory(id, editor) +} + +func (conductor *conductor) AddCategory(categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) { + return conductor.categoryManager.AddCategory(categoryTypeId, categoryId, title, description, editor) +} + +func (conductor *conductor) AddCategoryAtomic(transaction ITransaction, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) { + return conductor.categoryManager.AddCategoryAtomic(transaction, categoryTypeId, categoryId, title, description, editor) +} + +func (conductor *conductor) LogCategory(categoryTypeId int64, categoryId int64, title string, description string, source string, editor Identity, payload string) { + conductor.categoryManager.Log(categoryTypeId, categoryId, title, description, source, editor, payload) +} + +func (conductor *conductor) UpdateCategory(id int64, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) { + return conductor.categoryManager.UpdateCategory(id, categoryTypeId, categoryId, title, description, editor) +} + +func (conductor *conductor) UpdateCategoryAtomic(transaction ITransaction, id int64, categoryTypeId int64, categoryId int64, title string, description string, editor Identity) (ICategory, error) { + return conductor.categoryManager.UpdateCategoryAtomic(transaction, id, categoryTypeId, categoryId, title, description, editor) +} + +func (conductor *conductor) RemoveCategory(id int64, editor Identity) (ICategory, error) { + return conductor.categoryManager.RemoveCategory(id, editor) +} + +func (conductor *conductor) RemoveCategoryAtomic(transaction ITransaction, id int64, editor Identity) (ICategory, error) { + return conductor.categoryManager.RemoveCategoryAtomic(transaction, id, editor) +} + +func (conductor *conductor) ListCategoriesByCategoryType(categoryTypeId int64, pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryCollection { + return conductor.categoryManager.ListCategoriesByCategoryType(categoryTypeId, pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) ForEachCategoryByCategoryType(categoryTypeId int64, iterator CategoryIterator) { + conductor.categoryManager.ForEachByCategoryType(categoryTypeId, iterator) +} + +func (conductor *conductor) ListCategoriesByCategory(categoryId int64, pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICategoryCollection { + return conductor.categoryManager.ListCategoriesByCategory(categoryId, pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) ForEachCategoryByCategory(categoryId int64, iterator CategoryIterator) { + conductor.categoryManager.ForEachByCategory(categoryId, iterator) +} + +// User + +func (conductor *conductor) UserManager() IUserManager { + return conductor.userManager +} + +func (conductor *conductor) UserExists(id int64) bool { + return conductor.userManager.Exists(id) +} + +func (conductor *conductor) ListUsers(pageIndex uint32, pageSize uint32, criteria string, editor Identity) IUserCollection { + return conductor.userManager.ListUsers(pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) GetUser(id int64, editor Identity) (IUser, error) { + return conductor.userManager.GetUser(id, editor) +} + +func (conductor *conductor) AddUser(identityId int64, github string, editor Identity) (IUser, error) { + return conductor.userManager.AddUser(identityId, github, editor) +} + +func (conductor *conductor) AddUserAtomic(transaction ITransaction, identityId int64, github string, editor Identity) (IUser, error) { + return conductor.userManager.AddUserAtomic(transaction, identityId, github, editor) +} + +func (conductor *conductor) LogUser(identityId int64, github string, source string, editor Identity, payload string) { + conductor.userManager.Log(identityId, github, source, editor, payload) +} + +func (conductor *conductor) UpdateUser(id int64, github string, editor Identity) (IUser, error) { + return conductor.userManager.UpdateUser(id, github, editor) +} + +func (conductor *conductor) UpdateUserAtomic(transaction ITransaction, id int64, github string, editor Identity) (IUser, error) { + return conductor.userManager.UpdateUserAtomic(transaction, id, github, editor) +} + +func (conductor *conductor) RemoveUser(id int64, editor Identity) (IUser, error) { + return conductor.userManager.RemoveUser(id, editor) +} + +func (conductor *conductor) RemoveUserAtomic(transaction ITransaction, id int64, editor Identity) (IUser, error) { + return conductor.userManager.RemoveUserAtomic(transaction, id, editor) +} + +// Spi + +func (conductor *conductor) SpiManager() ISpiManager { + return conductor.spiManager +} + +func (conductor *conductor) SpiExists(id int64) bool { + return conductor.spiManager.Exists(id) +} + +func (conductor *conductor) ListSpis(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ISpiCollection { + return conductor.spiManager.ListSpis(pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) GetSpi(id int64, editor Identity) (ISpi, error) { + return conductor.spiManager.GetSpi(id, editor) +} + +func (conductor *conductor) AddSpi(editor Identity) (ISpi, error) { + return conductor.spiManager.AddSpi(editor) +} + +func (conductor *conductor) AddSpiAtomic(transaction ITransaction, editor Identity) (ISpi, error) { + return conductor.spiManager.AddSpiAtomic(transaction, editor) +} + +func (conductor *conductor) LogSpi(source string, editor Identity, payload string) { + conductor.spiManager.Log(source, editor, payload) +} + +func (conductor *conductor) UpdateSpi(id int64, editor Identity) (ISpi, error) { + return conductor.spiManager.UpdateSpi(id, editor) +} + +func (conductor *conductor) UpdateSpiAtomic(transaction ITransaction, id int64, editor Identity) (ISpi, error) { + return conductor.spiManager.UpdateSpiAtomic(transaction, id, editor) +} + +func (conductor *conductor) RemoveSpi(id int64, editor Identity) (ISpi, error) { + return conductor.spiManager.RemoveSpi(id, editor) +} + +func (conductor *conductor) RemoveSpiAtomic(transaction ITransaction, id int64, editor Identity) (ISpi, error) { + return conductor.spiManager.RemoveSpiAtomic(transaction, id, editor) +} + +func (conductor *conductor) Echo(document IDocument, editor Identity) (IEchoResult, error) { + return conductor.spiManager.Echo(document, editor) +} + +// CustomError + +func (conductor *conductor) CustomErrorManager() ICustomErrorManager { + return conductor.customErrorManager +} + +func (conductor *conductor) CustomErrorExists(id int64) bool { + return conductor.customErrorManager.Exists(id) +} + +func (conductor *conductor) ListCustomErrors(pageIndex uint32, pageSize uint32, criteria string, editor Identity) ICustomErrorCollection { + return conductor.customErrorManager.ListCustomErrors(pageIndex, pageSize, criteria, editor) +} + +func (conductor *conductor) GetCustomError(id int64, editor Identity) (ICustomError, error) { + return conductor.customErrorManager.GetCustomError(id, editor) +} + +func (conductor *conductor) AddCustomError(editor Identity) (ICustomError, error) { + return conductor.customErrorManager.AddCustomError(editor) +} + +func (conductor *conductor) AddCustomErrorAtomic(transaction ITransaction, editor Identity) (ICustomError, error) { + return conductor.customErrorManager.AddCustomErrorAtomic(transaction, editor) +} + +func (conductor *conductor) LogCustomError(source string, editor Identity, payload string) { + conductor.customErrorManager.Log(source, editor, payload) +} + +func (conductor *conductor) UpdateCustomError(id int64, editor Identity) (ICustomError, error) { + return conductor.customErrorManager.UpdateCustomError(id, editor) +} + +func (conductor *conductor) UpdateCustomErrorAtomic(transaction ITransaction, id int64, editor Identity) (ICustomError, error) { + return conductor.customErrorManager.UpdateCustomErrorAtomic(transaction, id, editor) +} + +func (conductor *conductor) RemoveCustomError(id int64, editor Identity) (ICustomError, error) { + return conductor.customErrorManager.RemoveCustomError(id, editor) +} + +func (conductor *conductor) RemoveCustomErrorAtomic(transaction ITransaction, id int64, editor Identity) (ICustomError, error) { + return conductor.customErrorManager.RemoveCustomErrorAtomic(transaction, id, editor) +} + +func (conductor *conductor) ResolveError(document IDocument, editor Identity) (IResolveErrorResult, error) { + return conductor.customErrorManager.ResolveError(document, editor) +} + +func (conductor *conductor) NewDocument(id int64, content string) (IDocument, error) { + return NewDocument(id, content) +} + +func (conductor *conductor) NewSystemSchedule(id int64, enabled bool, config string) (ISystemSchedule, error) { + return NewSystemSchedule(id, enabled, config) +} + +func (conductor *conductor) NewIdentity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) (IIdentity, error) { + return NewIdentity(id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount) +} + +func (conductor *conductor) NewAccessControl(id int64, key uint64, value uint64) (IAccessControl, error) { + return NewAccessControl(id, key, value) +} + +func (conductor *conductor) NewRemoteActivity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) (IRemoteActivity, error) { + return NewRemoteActivity(id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp) +} + +func (conductor *conductor) NewCategoryType(id int64, description string) (ICategoryType, error) { + return NewCategoryType(id, description) +} + +func (conductor *conductor) NewCategory(id int64, categoryTypeId int64, categoryId int64, title string, description string) (ICategory, error) { + return NewCategory(id, categoryTypeId, categoryId, title, description) +} + +func (conductor *conductor) NewUser(id int64, github string) (IUser, error) { + return NewUser(id, github) +} + +func (conductor *conductor) NewSpi() (ISpi, error) { + return NewSpi() +} + +func (conductor *conductor) NewCustomError() (ICustomError, error) { + return NewCustomError() +} + +func (conductor *conductor) NewEchoResult(document IDocument, _ interface{}) IEchoResult { + return NewEchoResult(document, nil) +} + +func (conductor *conductor) NewResolveErrorResult(_ interface{}) IResolveErrorResult { + return NewResolveErrorResult(nil) +} + +func (conductor *conductor) LogRemoteCall(context IContext, eventType uint32, source string, input, result interface{}, err error) { + errorMessage := "" + if err != nil { + errorMessage = err.Error() + if strings.HasPrefix(errorMessage, "ERROR_MESSAGE_") { + errorMessage = errorMessage[14:] + } + } + + if _, marshalError := json.Marshal(input); marshalError != nil { + input = fmt.Sprintf("%s", input) + context.Logger().Error(fmt.Sprintf("LRC_JSON_INPUT: %s %s", marshalError, input)) + } + + if _, marshalError := json.Marshal(result); marshalError != nil { + result = fmt.Sprintf("%s", result) + context.Logger().Error(fmt.Sprintf("LRC_JSON_RESULT: %s %s", marshalError, result)) + } + + data, marshalError := json.Marshal(&struct { + Operation string `json:"operation"` + Identity int64 `json:"identity"` + Token string `json:"token"` + RequestId uint64 `json:"request_id"` + ClientName string `json:"client_name"` + ClientVersion int32 `json:"client_version"` + ClientLatestVersion int32 `json:"client_latest_version"` + ServerVersion int32 `json:"server_version"` + ApiVersion int32 `json:"api_version"` + Input interface{} `json:"input"` + Result interface{} `json:"result"` + Error string `json:"error"` + Timestamp int64 `json:"timestamp"` + }{ + Operation: source, + Identity: context.Identity().Id(), + Token: context.Token(), + RequestId: context.RequestId(), + ClientName: context.ClientName(), + ClientVersion: context.ClientVersion(), + ClientLatestVersion: context.ClientLatestVersion(), + ServerVersion: context.ServerVersion(), + ApiVersion: context.ApiVersion(), + Input: input, + Result: result, + Error: errorMessage, + Timestamp: context.Timestamp().UnixNano(), + }) + + if marshalError != nil { + data = []byte("{}") + context.Logger().Error(fmt.Sprintf("LRC_JSON: %s %s %s", marshalError, input, result)) + } + + identity := context.Identity() + conductor.RemoteActivityManager().Log(source, time.Since(context.Timestamp()).Nanoseconds(), err == nil, errorMessage, identity.RemoteAddress(), identity.UserAgent(), eventType, context.Timestamp().UnixNano(), source, identity, string(data)) +} + +//endregion + +//region IAssertionResult Implementation + +type assertionResult struct { + condition bool +} + +func (result *assertionResult) Or(err error) { + if !result.condition { + panic(err.Error()) + } +} + +//endregion diff --git a/greataped/components/core/object.go b/greataped/components/core/object.go new file mode 100644 index 0000000..03f99e9 --- /dev/null +++ b/greataped/components/core/object.go @@ -0,0 +1,84 @@ +package core + +import ( + "fmt" + "sync" + "sync/atomic" + + "github.com/xeronith/diamante/logging" +) + +type object struct { + mutex sync.RWMutex + id int64 + keychain map[uint64]*semaphore +} + +func (object *object) Id() int64 { + return object.id +} + +func (object *object) Lock(context uint64) { + var key *semaphore + + func() { + object.mutex.Lock() + defer object.mutex.Unlock() + + if object.keychain == nil { + object.keychain = make(map[uint64]*semaphore) + } + + exists := false + key, exists = object.keychain[context] + if !exists { + key = &semaphore{} + object.keychain[context] = key + } + }() + + if !key.Acquire() { + logging.GetDefaultLogger().Panic(fmt.Sprintf("failed_to_acquire_exclusive_lock: 0x%.8X %d %d", context, object.id, len(object.keychain))) + panic("IDENTICAL_CONCURRENT_REQUESTS_NOT_ALLOWED") + } +} + +func (object *object) Unlock(context uint64) { + var key *semaphore + + func() { + object.mutex.RLock() + defer object.mutex.RUnlock() + + if object.keychain == nil { + logging.GetDefaultLogger().Panic("failed_to_release_exclusive_lock: empty_keychain") + return + } + + exists := false + key, exists = object.keychain[context] + if !exists { + logging.GetDefaultLogger().Panic("failed_to_release_exclusive_lock: invalid_key") + return + } + }() + + if !key.Release() { + logging.GetDefaultLogger().Warning(fmt.Sprintf("exclusive_lock_already_released: 0x%.8X %d %d", context, object.id, len(object.keychain))) + } +} + +// ----------------------------------------------------------- + +type semaphore struct { + locked int32 +} + +func (semaphore *semaphore) Acquire() bool { + return atomic.CompareAndSwapInt32(&semaphore.locked, 0, 1) +} +func (semaphore *semaphore) Release() bool { + return atomic.CompareAndSwapInt32(&semaphore.locked, 1, 0) +} + +// ----------------------------------------------------------- diff --git a/greataped/components/core/remote_activity.go b/greataped/components/core/remote_activity.go new file mode 100644 index 0000000..bd28526 --- /dev/null +++ b/greataped/components/core/remote_activity.go @@ -0,0 +1,456 @@ +package core + +import ( + "fmt" + + . "github.com/xeronith/diamante/contracts/security" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + "rail.town/infrastructure/components/model/repository" +) + +type remoteActivity struct { + object + entryPoint string + duration int64 + successful bool + errorMessage string + remoteAddress string + userAgent string + eventType uint32 + timestamp int64 +} + +// noinspection GoUnusedExportedFunction +func InitializeRemoteActivity() { + _ = ENABLE_SECURITY + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + _ = repository.Initialize +} + +func NewRemoteActivity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) (IRemoteActivity, error) { + instance := &remoteActivity{ + object: object{ + id: id, + }, + entryPoint: entryPoint, + duration: duration, + successful: successful, + errorMessage: errorMessage, + remoteAddress: remoteAddress, + userAgent: userAgent, + eventType: eventType, + timestamp: timestamp, + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func NewRemoteActivityFromEntity(entity IRemoteActivityEntity) (IRemoteActivity, error) { + instance := &remoteActivity{ + object: object{ + id: entity.Id(), + }, + entryPoint: entity.EntryPoint(), + duration: entity.Duration(), + successful: entity.Successful(), + errorMessage: entity.ErrorMessage(), + remoteAddress: entity.RemoteAddress(), + userAgent: entity.UserAgent(), + eventType: entity.EventType(), + timestamp: entity.Timestamp(), + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func (remoteActivity *remoteActivity) EntryPoint() string { + return remoteActivity.entryPoint +} + +func (remoteActivity *remoteActivity) UpdateEntryPoint(entryPoint string, editor Identity) { + if err := repository.RemoteActivities.UpdateEntryPoint(remoteActivity.id, entryPoint, editor.Id()); err != nil { + panic(err.Error()) + } + + remoteActivity.entryPoint = entryPoint +} + +func (remoteActivity *remoteActivity) UpdateEntryPointAtomic(transaction ITransaction, entryPoint string, editor Identity) { + transaction.OnCommit(func() { + remoteActivity.entryPoint = entryPoint + }) + + if err := repository.RemoteActivities.UpdateEntryPointAtomic(transaction, remoteActivity.id, entryPoint, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (remoteActivity *remoteActivity) Duration() int64 { + return remoteActivity.duration +} + +func (remoteActivity *remoteActivity) UpdateDuration(duration int64, editor Identity) { + if err := repository.RemoteActivities.UpdateDuration(remoteActivity.id, duration, editor.Id()); err != nil { + panic(err.Error()) + } + + remoteActivity.duration = duration +} + +func (remoteActivity *remoteActivity) UpdateDurationAtomic(transaction ITransaction, duration int64, editor Identity) { + transaction.OnCommit(func() { + remoteActivity.duration = duration + }) + + if err := repository.RemoteActivities.UpdateDurationAtomic(transaction, remoteActivity.id, duration, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (remoteActivity *remoteActivity) Successful() bool { + return remoteActivity.successful +} + +func (remoteActivity *remoteActivity) UpdateSuccessful(successful bool, editor Identity) { + if err := repository.RemoteActivities.UpdateSuccessful(remoteActivity.id, successful, editor.Id()); err != nil { + panic(err.Error()) + } + + remoteActivity.successful = successful +} + +func (remoteActivity *remoteActivity) UpdateSuccessfulAtomic(transaction ITransaction, successful bool, editor Identity) { + transaction.OnCommit(func() { + remoteActivity.successful = successful + }) + + if err := repository.RemoteActivities.UpdateSuccessfulAtomic(transaction, remoteActivity.id, successful, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (remoteActivity *remoteActivity) ErrorMessage() string { + return remoteActivity.errorMessage +} + +func (remoteActivity *remoteActivity) UpdateErrorMessage(errorMessage string, editor Identity) { + if err := repository.RemoteActivities.UpdateErrorMessage(remoteActivity.id, errorMessage, editor.Id()); err != nil { + panic(err.Error()) + } + + remoteActivity.errorMessage = errorMessage +} + +func (remoteActivity *remoteActivity) UpdateErrorMessageAtomic(transaction ITransaction, errorMessage string, editor Identity) { + transaction.OnCommit(func() { + remoteActivity.errorMessage = errorMessage + }) + + if err := repository.RemoteActivities.UpdateErrorMessageAtomic(transaction, remoteActivity.id, errorMessage, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (remoteActivity *remoteActivity) RemoteAddress() string { + return remoteActivity.remoteAddress +} + +func (remoteActivity *remoteActivity) UpdateRemoteAddress(remoteAddress string, editor Identity) { + if err := repository.RemoteActivities.UpdateRemoteAddress(remoteActivity.id, remoteAddress, editor.Id()); err != nil { + panic(err.Error()) + } + + remoteActivity.remoteAddress = remoteAddress +} + +func (remoteActivity *remoteActivity) UpdateRemoteAddressAtomic(transaction ITransaction, remoteAddress string, editor Identity) { + transaction.OnCommit(func() { + remoteActivity.remoteAddress = remoteAddress + }) + + if err := repository.RemoteActivities.UpdateRemoteAddressAtomic(transaction, remoteActivity.id, remoteAddress, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (remoteActivity *remoteActivity) UserAgent() string { + return remoteActivity.userAgent +} + +func (remoteActivity *remoteActivity) UpdateUserAgent(userAgent string, editor Identity) { + if err := repository.RemoteActivities.UpdateUserAgent(remoteActivity.id, userAgent, editor.Id()); err != nil { + panic(err.Error()) + } + + remoteActivity.userAgent = userAgent +} + +func (remoteActivity *remoteActivity) UpdateUserAgentAtomic(transaction ITransaction, userAgent string, editor Identity) { + transaction.OnCommit(func() { + remoteActivity.userAgent = userAgent + }) + + if err := repository.RemoteActivities.UpdateUserAgentAtomic(transaction, remoteActivity.id, userAgent, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (remoteActivity *remoteActivity) EventType() uint32 { + return remoteActivity.eventType +} + +func (remoteActivity *remoteActivity) UpdateEventType(eventType uint32, editor Identity) { + if err := repository.RemoteActivities.UpdateEventType(remoteActivity.id, eventType, editor.Id()); err != nil { + panic(err.Error()) + } + + remoteActivity.eventType = eventType +} + +func (remoteActivity *remoteActivity) UpdateEventTypeAtomic(transaction ITransaction, eventType uint32, editor Identity) { + transaction.OnCommit(func() { + remoteActivity.eventType = eventType + }) + + if err := repository.RemoteActivities.UpdateEventTypeAtomic(transaction, remoteActivity.id, eventType, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (remoteActivity *remoteActivity) Timestamp() int64 { + return remoteActivity.timestamp +} + +func (remoteActivity *remoteActivity) UpdateTimestamp(timestamp int64, editor Identity) { + if err := repository.RemoteActivities.UpdateTimestamp(remoteActivity.id, timestamp, editor.Id()); err != nil { + panic(err.Error()) + } + + remoteActivity.timestamp = timestamp +} + +func (remoteActivity *remoteActivity) UpdateTimestampAtomic(transaction ITransaction, timestamp int64, editor Identity) { + transaction.OnCommit(func() { + remoteActivity.timestamp = timestamp + }) + + if err := repository.RemoteActivities.UpdateTimestampAtomic(transaction, remoteActivity.id, timestamp, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (remoteActivity *remoteActivity) Validate() error { + return nil +} + +func (remoteActivity *remoteActivity) String() string { + return fmt.Sprintf("RemoteActivity (Id: %d, EntryPoint: %v, Duration: %v, Successful: %v, ErrorMessage: %v, RemoteAddress: %v, UserAgent: %v, EventType: %v, Timestamp: %v)", remoteActivity.Id(), remoteActivity.EntryPoint(), remoteActivity.Duration(), remoteActivity.Successful(), remoteActivity.ErrorMessage(), remoteActivity.RemoteAddress(), remoteActivity.UserAgent(), remoteActivity.EventType(), remoteActivity.Timestamp()) +} + +//------------------------------------------------------------------------------ + +type remoteActivities struct { + collection RemoteActivities +} + +// NewRemoteActivities creates an empty collection of 'Remote Activity' which is not thread-safe. +func NewRemoteActivities() IRemoteActivityCollection { + return &remoteActivities{ + collection: make(RemoteActivities, 0), + } +} + +func (remoteActivities *remoteActivities) Count() int { + return len(remoteActivities.collection) +} + +func (remoteActivities *remoteActivities) IsEmpty() bool { + return len(remoteActivities.collection) == 0 +} + +func (remoteActivities *remoteActivities) IsNotEmpty() bool { + return len(remoteActivities.collection) > 0 +} + +func (remoteActivities *remoteActivities) HasExactlyOneItem() bool { + return len(remoteActivities.collection) == 1 +} + +func (remoteActivities *remoteActivities) HasAtLeastOneItem() bool { + return len(remoteActivities.collection) >= 1 +} + +func (remoteActivities *remoteActivities) First() IRemoteActivity { + return remoteActivities.collection[0] +} + +func (remoteActivities *remoteActivities) Append(remoteActivity IRemoteActivity) { + remoteActivities.collection = append(remoteActivities.collection, remoteActivity) +} + +func (remoteActivities *remoteActivities) ForEach(iterator RemoteActivityIterator) { + if iterator == nil { + return + } + + for _, value := range remoteActivities.collection { + iterator(value) + } +} + +func (remoteActivities *remoteActivities) Array() RemoteActivities { + return remoteActivities.collection +} + +//------------------------------------------------------------------------------ + +func (dispatcher *dispatcher) RemoteActivityExists(id int64) bool { + return dispatcher.conductor.RemoteActivityManager().Exists(id) +} + +func (dispatcher *dispatcher) RemoteActivityExistsWhich(condition RemoteActivityCondition) bool { + return dispatcher.conductor.RemoteActivityManager().ExistsWhich(condition) +} + +func (dispatcher *dispatcher) ListRemoteActivities() IRemoteActivityCollection { + return dispatcher.conductor.RemoteActivityManager().ListRemoteActivities(0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachRemoteActivity(iterator RemoteActivityIterator) { + dispatcher.conductor.RemoteActivityManager().ForEach(iterator) +} + +func (dispatcher *dispatcher) FilterRemoteActivities(predicate RemoteActivityFilterPredicate) IRemoteActivityCollection { + return dispatcher.conductor.RemoteActivityManager().Filter(predicate) +} + +func (dispatcher *dispatcher) MapRemoteActivities(predicate RemoteActivityMapPredicate) IRemoteActivityCollection { + return dispatcher.conductor.RemoteActivityManager().Map(predicate) +} + +func (dispatcher *dispatcher) GetRemoteActivity(id int64) IRemoteActivity { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().GetRemoteActivity(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } +} + +func (dispatcher *dispatcher) AddRemoteActivity(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) IRemoteActivity { + transaction := dispatcher.transaction + if transaction != nil { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().AddRemoteActivityAtomic(transaction, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } else { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().AddRemoteActivity(entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } +} + +func (dispatcher *dispatcher) AddRemoteActivityWithCustomId(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) IRemoteActivity { + transaction := dispatcher.transaction + if transaction != nil { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().AddRemoteActivityWithCustomIdAtomic(id, transaction, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } else { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().AddRemoteActivityWithCustomId(id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } +} + +func (dispatcher *dispatcher) LogRemoteActivity(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, source string, payload string) { + dispatcher.conductor.RemoteActivityManager().Log(entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, source, dispatcher.identity, payload) +} + +func (dispatcher *dispatcher) UpdateRemoteActivity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) IRemoteActivity { + transaction := dispatcher.transaction + if transaction != nil { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().UpdateRemoteActivityAtomic(transaction, id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } else { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().UpdateRemoteActivity(id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } +} + +// noinspection GoUnusedParameter +func (dispatcher *dispatcher) UpdateRemoteActivityObject(object IObject, remoteActivity IRemoteActivity) IRemoteActivity { + transaction := dispatcher.transaction + if transaction != nil { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().UpdateRemoteActivityAtomic(transaction, object.Id(), remoteActivity.EntryPoint(), remoteActivity.Duration(), remoteActivity.Successful(), remoteActivity.ErrorMessage(), remoteActivity.RemoteAddress(), remoteActivity.UserAgent(), remoteActivity.EventType(), remoteActivity.Timestamp(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } else { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().UpdateRemoteActivity(object.Id(), remoteActivity.EntryPoint(), remoteActivity.Duration(), remoteActivity.Successful(), remoteActivity.ErrorMessage(), remoteActivity.RemoteAddress(), remoteActivity.UserAgent(), remoteActivity.EventType(), remoteActivity.Timestamp(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } +} + +func (dispatcher *dispatcher) AddOrUpdateRemoteActivityObject(object IObject, remoteActivity IRemoteActivity) IRemoteActivity { + transaction := dispatcher.transaction + if transaction != nil { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().AddOrUpdateRemoteActivityObjectAtomic(transaction, object.Id(), remoteActivity, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } else { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().AddOrUpdateRemoteActivityObject(object.Id(), remoteActivity, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } +} + +func (dispatcher *dispatcher) RemoveRemoteActivity(id int64) IRemoteActivity { + transaction := dispatcher.transaction + if transaction != nil { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().RemoveRemoteActivityAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } else { + if remoteActivity, err := dispatcher.conductor.RemoteActivityManager().RemoveRemoteActivity(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return remoteActivity + } + } +} diff --git a/greataped/components/core/remote_activity_manager.go b/greataped/components/core/remote_activity_manager.go new file mode 100644 index 0000000..279dc4a --- /dev/null +++ b/greataped/components/core/remote_activity_manager.go @@ -0,0 +1,251 @@ +package core + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/system" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" + "rail.town/infrastructure/components/model/repository" +) + +// noinspection GoSnakeCaseUsage +const REMOTE_ACTIVITY_MANAGER = "RemoteActivityManager" + +type remoteActivityManager struct { + systemComponent + cache ICache +} + +func newRemoteActivityManager(configuration IConfiguration, logger ILogger, dependencies ...ISystemComponent) IRemoteActivityManager { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + + manager := &remoteActivityManager{ + systemComponent: newSystemComponent(configuration, logger), + cache: NewCache(), + } + + if err := manager.ResolveDependencies(dependencies...); err != nil { + return nil + } + + return manager +} + +func (manager *remoteActivityManager) Name() string { + return REMOTE_ACTIVITY_MANAGER +} + +func (manager *remoteActivityManager) ResolveDependencies(_ ...ISystemComponent) error { + return nil +} + +func (manager *remoteActivityManager) Load() error { + return nil +} + +func (manager *remoteActivityManager) Reload() error { + return manager.Load() +} + +func (manager *remoteActivityManager) OnCacheChanged(callback RemoteActivityCacheCallback) { + manager.cache.OnChanged(callback) +} + +func (manager *remoteActivityManager) Count() int { + return manager.cache.Size() +} + +func (manager *remoteActivityManager) Exists(id int64) bool { + return manager.Find(id) != nil +} + +func (manager *remoteActivityManager) ExistsWhich(condition RemoteActivityCondition) bool { + var remoteActivities RemoteActivities + manager.ForEach(func(remoteActivity IRemoteActivity) { + if condition(remoteActivity) { + remoteActivities = append(remoteActivities, remoteActivity) + } + }) + + return len(remoteActivities) > 0 +} + +func (manager *remoteActivityManager) ListRemoteActivities(_ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) IRemoteActivityCollection { + return manager.Filter(RemoteActivityPassThroughFilter) +} + +func (manager *remoteActivityManager) GetRemoteActivity(id int64, _ Identity) (IRemoteActivity, error) { + if remoteActivity := manager.Find(id); remoteActivity == nil { + return nil, ERROR_REMOTE_ACTIVITY_NOT_FOUND + } else { + return remoteActivity, nil + } +} + +func (manager *remoteActivityManager) AddRemoteActivity(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(manager.UniqueId(), entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp) + return manager.Apply(remoteActivityEntity, repository.RemoteActivities.Add, manager.cache.Put, editor) +} + +func (manager *remoteActivityManager) AddRemoteActivityWithCustomId(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp) + return manager.Apply(remoteActivityEntity, repository.RemoteActivities.Add, manager.cache.Put, editor) +} + +func (manager *remoteActivityManager) AddRemoteActivityObject(remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(manager.UniqueId(), remoteActivity.EntryPoint(), remoteActivity.Duration(), remoteActivity.Successful(), remoteActivity.ErrorMessage(), remoteActivity.RemoteAddress(), remoteActivity.UserAgent(), remoteActivity.EventType(), remoteActivity.Timestamp()) + return manager.Apply(remoteActivityEntity, repository.RemoteActivities.Add, manager.cache.Put, editor) +} + +func (manager *remoteActivityManager) AddRemoteActivityAtomic(transaction ITransaction, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(manager.UniqueId(), entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp) + return manager.ApplyAtomic(transaction, remoteActivityEntity, repository.RemoteActivities.AddAtomic, manager.cache.Put, editor) +} + +func (manager *remoteActivityManager) AddRemoteActivityWithCustomIdAtomic(id int64, transaction ITransaction, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp) + return manager.ApplyAtomic(transaction, remoteActivityEntity, repository.RemoteActivities.AddAtomic, manager.cache.Put, editor) +} + +func (manager *remoteActivityManager) AddRemoteActivityObjectAtomic(transaction ITransaction, remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(manager.UniqueId(), remoteActivity.EntryPoint(), remoteActivity.Duration(), remoteActivity.Successful(), remoteActivity.ErrorMessage(), remoteActivity.RemoteAddress(), remoteActivity.UserAgent(), remoteActivity.EventType(), remoteActivity.Timestamp()) + return manager.ApplyAtomic(transaction, remoteActivityEntity, repository.RemoteActivities.AddAtomic, manager.cache.Put, editor) +} + +func (manager *remoteActivityManager) Log(entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, source string, editor Identity, payload string) { + remoteActivityPipeEntity := NewRemoteActivityPipeEntity(manager.UniqueId(), entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp, source, editor.Id(), payload) + repository.Pipe.Insert(remoteActivityPipeEntity) + + remoteActivity, err := NewRemoteActivityFromEntity(remoteActivityPipeEntity) + if err != nil { + manager.Logger().Error(err) + } else { + manager.cache.Put(remoteActivity.Id(), remoteActivity) + } +} + +func (manager *remoteActivityManager) UpdateRemoteActivity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp) + return manager.Apply(remoteActivityEntity, repository.RemoteActivities.Update, manager.cache.Put, editor) +} + +func (manager *remoteActivityManager) UpdateRemoteActivityObject(id int64, remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(id, remoteActivity.EntryPoint(), remoteActivity.Duration(), remoteActivity.Successful(), remoteActivity.ErrorMessage(), remoteActivity.RemoteAddress(), remoteActivity.UserAgent(), remoteActivity.EventType(), remoteActivity.Timestamp()) + return manager.Apply(remoteActivityEntity, repository.RemoteActivities.Update, manager.cache.Put, editor) +} + +func (manager *remoteActivityManager) UpdateRemoteActivityAtomic(transaction ITransaction, id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp) + return manager.ApplyAtomic(transaction, remoteActivityEntity, repository.RemoteActivities.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *remoteActivityManager) UpdateRemoteActivityObjectAtomic(transaction ITransaction, id int64, remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(id, remoteActivity.EntryPoint(), remoteActivity.Duration(), remoteActivity.Successful(), remoteActivity.ErrorMessage(), remoteActivity.RemoteAddress(), remoteActivity.UserAgent(), remoteActivity.EventType(), remoteActivity.Timestamp()) + return manager.ApplyAtomic(transaction, remoteActivityEntity, repository.RemoteActivities.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *remoteActivityManager) AddOrUpdateRemoteActivityObject(id int64, remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) { + if manager.Exists(id) { + return manager.UpdateRemoteActivityObject(id, remoteActivity, editor) + } else { + return manager.AddRemoteActivityObject(remoteActivity, editor) + } +} + +func (manager *remoteActivityManager) AddOrUpdateRemoteActivityObjectAtomic(transaction ITransaction, id int64, remoteActivity IRemoteActivity, editor Identity) (IRemoteActivity, error) { + if manager.Exists(id) { + return manager.UpdateRemoteActivityObjectAtomic(transaction, id, remoteActivity, editor) + } else { + return manager.AddRemoteActivityObjectAtomic(transaction, remoteActivity, editor) + } +} + +func (manager *remoteActivityManager) RemoveRemoteActivity(id int64, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(id, "", 0, false, "", "", "", 0, 0) + return manager.Apply(remoteActivityEntity, repository.RemoteActivities.Remove, manager.cache.Remove, editor) +} + +func (manager *remoteActivityManager) RemoveRemoteActivityAtomic(transaction ITransaction, id int64, editor Identity) (IRemoteActivity, error) { + remoteActivityEntity := NewRemoteActivityEntity(id, "", 0, false, "", "", "", 0, 0) + return manager.ApplyAtomic(transaction, remoteActivityEntity, repository.RemoteActivities.RemoveAtomic, manager.cache.Remove, editor) +} + +func (manager *remoteActivityManager) Apply(remoteActivityEntity IRemoteActivityEntity, repositoryHandler func(IRemoteActivityEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (IRemoteActivity, error) { + result, err := NewRemoteActivityFromEntity(remoteActivityEntity) + if err != nil { + return nil, err + } + + if err := repositoryHandler(remoteActivityEntity, editor.Id()); err != nil { + return nil, err + } + + cacheHandler(result.Id(), result) + return result, nil +} + +func (manager *remoteActivityManager) ApplyAtomic(transaction ITransaction, remoteActivityEntity IRemoteActivityEntity, repositoryHandler func(IRepositoryTransaction, IRemoteActivityEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (IRemoteActivity, error) { + result, err := NewRemoteActivityFromEntity(remoteActivityEntity) + if err != nil { + return nil, err + } + + transaction.OnCommit(func() { + cacheHandler(result.Id(), result) + }) + + if err := repositoryHandler(transaction, remoteActivityEntity, editor.Id()); err != nil { + return nil, err + } + + return result, nil +} + +func (manager *remoteActivityManager) Find(id int64) IRemoteActivity { + if object, exists := manager.cache.Get(id); !exists { + return nil + } else { + return object.(IRemoteActivity) + } +} + +func (manager *remoteActivityManager) ForEach(iterator RemoteActivityIterator) { + manager.cache.ForEachValue(func(object ISystemObject) { + iterator(object.(IRemoteActivity)) + }) +} + +func (manager *remoteActivityManager) Filter(predicate RemoteActivityFilterPredicate) IRemoteActivityCollection { + remoteActivities := NewRemoteActivities() + if predicate == nil { + return remoteActivities + } + + manager.ForEach(func(remoteActivity IRemoteActivity) { + if predicate(remoteActivity) { + remoteActivities.Append(remoteActivity) + } + }) + + return remoteActivities +} + +func (manager *remoteActivityManager) Map(predicate RemoteActivityMapPredicate) IRemoteActivityCollection { + remoteActivities := NewRemoteActivities() + if predicate == nil { + return remoteActivities + } + + manager.ForEach(func(remoteActivity IRemoteActivity) { + remoteActivities.Append(predicate(remoteActivity)) + }) + + return remoteActivities +} diff --git a/greataped/components/core/remote_activity_manager_test.go b/greataped/components/core/remote_activity_manager_test.go new file mode 100644 index 0000000..69477fb --- /dev/null +++ b/greataped/components/core/remote_activity_manager_test.go @@ -0,0 +1,150 @@ +package core_test + +import ( + "testing" + + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/core" +) + +func TestRemoteActivityManager_GetName(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + if manager.Name() != REMOTE_ACTIVITY_MANAGER { + test.Fail() + } +} + +func TestRemoteActivityManager_ResolveDependencies(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + if err := manager.ResolveDependencies(); err != nil { + test.Fatal(err) + } +} + +func TestRemoteActivityManager_Load(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + if err := manager.Load(); err != nil { + test.Fatal(err) + } +} + +func TestRemoteActivityManager_Reload(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + if err := manager.Reload(); err != nil && err != ERROR_OPERATION_NOT_SUPPORTED { + test.Fatal(err) + } +} + +func TestRemoteActivityManager_Count(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + _ = manager.Count() +} + +func TestRemoteActivityManager_Exists(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + if manager.Exists(0) { + test.FailNow() + } +} + +func TestRemoteActivityManager_ListRemoteActivities(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + _ = manager.ListRemoteActivities(0, 0, "", nil) +} + +func TestRemoteActivityManager_GetRemoteActivity(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + if remoteActivity, err := manager.GetRemoteActivity(0, nil); err == nil { + _ = remoteActivity + test.FailNow() + } +} + +func TestRemoteActivityManager_AddRemoteActivity(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + remoteActivity, err := manager.AddRemoteActivity("entry_point", 0, true, "error_message", "remote_address", "user_agent", 0, 0, nil) + if err != nil { + test.Fatal(err) + } + + _ = remoteActivity +} + +func TestRemoteActivityManager_UpdateRemoteActivity(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + remoteActivity, err := manager.UpdateRemoteActivity(0, "entry_point", 0, true, "error_message", "remote_address", "user_agent", 0, 0, nil) + if err != nil { + test.Fatal(err) + } + + _ = remoteActivity +} + +func TestRemoteActivityManager_RemoveRemoteActivity(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + remoteActivity, err := manager.RemoveRemoteActivity(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = remoteActivity +} + +func TestRemoteActivityManager_Find(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + remoteActivity := manager.Find(0) + if remoteActivity == nil { + test.Fail() + } + + _ = remoteActivity +} + +func TestRemoteActivityManager_ForEach(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + manager.ForEach(func(remoteActivity IRemoteActivity) { + _ = remoteActivity + }) +} + +func TestRemoteActivityManager_Filter(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + remoteActivities := manager.Filter(func(remoteActivity IRemoteActivity) bool { + return remoteActivity.Id() < 0 + }) + + if remoteActivities.IsNotEmpty() { + test.Fail() + } + + _ = remoteActivities +} + +func TestRemoteActivityManager_Map(test *testing.T) { + manager := Conductor.RemoteActivityManager() + + remoteActivities := manager.Map(func(remoteActivity IRemoteActivity) IRemoteActivity { + return remoteActivity + }) + + if remoteActivities.Count() != manager.Count() { + test.Fail() + } + + _ = remoteActivities +} diff --git a/greataped/components/core/spi.go b/greataped/components/core/spi.go new file mode 100644 index 0000000..a6f3ad3 --- /dev/null +++ b/greataped/components/core/spi.go @@ -0,0 +1,240 @@ +package core + +import ( + "fmt" + + . "github.com/xeronith/diamante/contracts/security" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + "rail.town/infrastructure/components/model/repository" +) + +type spi struct { +} + +// noinspection GoUnusedExportedFunction +func InitializeSpi() { + _ = ENABLE_SECURITY + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + _ = repository.Initialize +} + +func NewSpi() (ISpi, error) { + instance := &spi{} + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func (spi *spi) Validate() error { + return nil +} + +func (spi *spi) String() string { + return fmt.Sprintf("Spi (Id: %d)", 0) +} + +//------------------------------------------------------------------------------ + +type spis struct { + collection Spis +} + +// NewSpis creates an empty collection of 'Spi' which is not thread-safe. +func NewSpis() ISpiCollection { + return &spis{ + collection: make(Spis, 0), + } +} + +func (spis *spis) Count() int { + return len(spis.collection) +} + +func (spis *spis) IsEmpty() bool { + return len(spis.collection) == 0 +} + +func (spis *spis) IsNotEmpty() bool { + return len(spis.collection) > 0 +} + +func (spis *spis) HasExactlyOneItem() bool { + return len(spis.collection) == 1 +} + +func (spis *spis) HasAtLeastOneItem() bool { + return len(spis.collection) >= 1 +} + +func (spis *spis) First() ISpi { + return spis.collection[0] +} + +func (spis *spis) Append(spi ISpi) { + spis.collection = append(spis.collection, spi) +} + +func (spis *spis) ForEach(iterator SpiIterator) { + if iterator == nil { + return + } + + for _, value := range spis.collection { + iterator(value) + } +} + +func (spis *spis) Array() Spis { + return spis.collection +} + +//------------------------------------------------------------------------------ + +func (dispatcher *dispatcher) SpiExists(id int64) bool { + return dispatcher.conductor.SpiManager().Exists(id) +} + +func (dispatcher *dispatcher) SpiExistsWhich(condition SpiCondition) bool { + return dispatcher.conductor.SpiManager().ExistsWhich(condition) +} + +func (dispatcher *dispatcher) ListSpis() ISpiCollection { + return dispatcher.conductor.SpiManager().ListSpis(0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachSpi(iterator SpiIterator) { + dispatcher.conductor.SpiManager().ForEach(iterator) +} + +func (dispatcher *dispatcher) FilterSpis(predicate SpiFilterPredicate) ISpiCollection { + return dispatcher.conductor.SpiManager().Filter(predicate) +} + +func (dispatcher *dispatcher) MapSpis(predicate SpiMapPredicate) ISpiCollection { + return dispatcher.conductor.SpiManager().Map(predicate) +} + +func (dispatcher *dispatcher) GetSpi(id int64) ISpi { + if spi, err := dispatcher.conductor.SpiManager().GetSpi(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } +} + +func (dispatcher *dispatcher) AddSpi() ISpi { + transaction := dispatcher.transaction + if transaction != nil { + if spi, err := dispatcher.conductor.SpiManager().AddSpiAtomic(transaction, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } else { + if spi, err := dispatcher.conductor.SpiManager().AddSpi(dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } +} + +func (dispatcher *dispatcher) AddSpiWithCustomId(id int64) ISpi { + transaction := dispatcher.transaction + if transaction != nil { + if spi, err := dispatcher.conductor.SpiManager().AddSpiWithCustomIdAtomic(id, transaction, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } else { + if spi, err := dispatcher.conductor.SpiManager().AddSpiWithCustomId(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } +} + +func (dispatcher *dispatcher) LogSpi(source string, payload string) { + dispatcher.conductor.SpiManager().Log(source, dispatcher.identity, payload) +} + +func (dispatcher *dispatcher) UpdateSpi(id int64) ISpi { + transaction := dispatcher.transaction + if transaction != nil { + if spi, err := dispatcher.conductor.SpiManager().UpdateSpiAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } else { + if spi, err := dispatcher.conductor.SpiManager().UpdateSpi(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } +} + +// noinspection GoUnusedParameter +func (dispatcher *dispatcher) UpdateSpiObject(object IObject, spi ISpi) ISpi { + transaction := dispatcher.transaction + if transaction != nil { + if spi, err := dispatcher.conductor.SpiManager().UpdateSpiAtomic(transaction, object.Id(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } else { + if spi, err := dispatcher.conductor.SpiManager().UpdateSpi(object.Id(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } +} + +func (dispatcher *dispatcher) AddOrUpdateSpiObject(object IObject, spi ISpi) ISpi { + transaction := dispatcher.transaction + if transaction != nil { + if spi, err := dispatcher.conductor.SpiManager().AddOrUpdateSpiObjectAtomic(transaction, object.Id(), spi, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } else { + if spi, err := dispatcher.conductor.SpiManager().AddOrUpdateSpiObject(object.Id(), spi, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } +} + +func (dispatcher *dispatcher) RemoveSpi(id int64) ISpi { + transaction := dispatcher.transaction + if transaction != nil { + if spi, err := dispatcher.conductor.SpiManager().RemoveSpiAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } else { + if spi, err := dispatcher.conductor.SpiManager().RemoveSpi(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return spi + } + } +} + +func (dispatcher *dispatcher) Echo(document IDocument) (IEchoResult, error) { + return dispatcher.conductor.SpiManager().Echo(document, dispatcher.identity) +} diff --git a/greataped/components/core/spi_manager.go b/greataped/components/core/spi_manager.go new file mode 100644 index 0000000..6a53eed --- /dev/null +++ b/greataped/components/core/spi_manager.go @@ -0,0 +1,232 @@ +package core + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/system" + commands "rail.town/infrastructure/app/commands/spi" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" +) + +// noinspection GoSnakeCaseUsage +const SPI_MANAGER = "SpiManager" + +type spiManager struct { + systemComponent + cache ICache +} + +func newSpiManager(configuration IConfiguration, logger ILogger, dependencies ...ISystemComponent) ISpiManager { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + + manager := &spiManager{ + systemComponent: newSystemComponent(configuration, logger), + cache: NewCache(), + } + + if err := manager.ResolveDependencies(dependencies...); err != nil { + return nil + } + + return manager +} + +func (manager *spiManager) Name() string { + return SPI_MANAGER +} + +func (manager *spiManager) ResolveDependencies(_ ...ISystemComponent) error { + return nil +} + +func (manager *spiManager) Load() error { + return nil +} + +func (manager *spiManager) Reload() error { + return manager.Load() +} + +func (manager *spiManager) OnCacheChanged(callback SpiCacheCallback) { + manager.cache.OnChanged(callback) +} + +func (manager *spiManager) Count() int { + return manager.cache.Size() +} + +func (manager *spiManager) Exists(id int64) bool { + return manager.Find(id) != nil +} + +func (manager *spiManager) ExistsWhich(condition SpiCondition) bool { + var spis Spis + manager.ForEach(func(spi ISpi) { + if condition(spi) { + spis = append(spis, spi) + } + }) + + return len(spis) > 0 +} + +func (manager *spiManager) ListSpis(_ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) ISpiCollection { + return manager.Filter(SpiPassThroughFilter) +} + +func (manager *spiManager) GetSpi(id int64, _ Identity) (ISpi, error) { + if spi := manager.Find(id); spi == nil { + return nil, ERROR_SPI_NOT_FOUND + } else { + return spi, nil + } +} + +func (manager *spiManager) AddSpi(editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) AddSpiWithCustomId(id int64, editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) AddSpiObject(spi ISpi, editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) AddSpiAtomic(transaction ITransaction, editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) AddSpiWithCustomIdAtomic(id int64, transaction ITransaction, editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) AddSpiObjectAtomic(transaction ITransaction, spi ISpi, editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) Log(source string, editor Identity, payload string) { +} + +func (manager *spiManager) UpdateSpi(id int64, editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) UpdateSpiObject(id int64, spi ISpi, editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) UpdateSpiAtomic(transaction ITransaction, id int64, editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) UpdateSpiObjectAtomic(transaction ITransaction, id int64, spi ISpi, editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) AddOrUpdateSpiObject(id int64, spi ISpi, editor Identity) (ISpi, error) { + if manager.Exists(id) { + return manager.UpdateSpiObject(id, spi, editor) + } else { + return manager.AddSpiObject(spi, editor) + } +} + +func (manager *spiManager) AddOrUpdateSpiObjectAtomic(transaction ITransaction, id int64, spi ISpi, editor Identity) (ISpi, error) { + if manager.Exists(id) { + return manager.UpdateSpiObjectAtomic(transaction, id, spi, editor) + } else { + return manager.AddSpiObjectAtomic(transaction, spi, editor) + } +} + +func (manager *spiManager) RemoveSpi(id int64, editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) RemoveSpiAtomic(transaction ITransaction, id int64, editor Identity) (ISpi, error) { + return nil, ERROR_NOT_IMPLEMENTED +} + +func (manager *spiManager) Find(id int64) ISpi { + if object, exists := manager.cache.Get(id); !exists { + return nil + } else { + return object.(ISpi) + } +} + +func (manager *spiManager) ForEach(iterator SpiIterator) { + manager.cache.ForEachValue(func(object ISystemObject) { + iterator(object.(ISpi)) + }) +} + +func (manager *spiManager) Filter(predicate SpiFilterPredicate) ISpiCollection { + spis := NewSpis() + if predicate == nil { + return spis + } + + manager.ForEach(func(spi ISpi) { + if predicate(spi) { + spis.Append(spi) + } + }) + + return spis +} + +func (manager *spiManager) Map(predicate SpiMapPredicate) ISpiCollection { + spis := NewSpis() + if predicate == nil { + return spis + } + + manager.ForEach(func(spi ISpi) { + spis.Append(predicate(spi)) + }) + + return spis +} + +//region IEchoResult Implementation + +type echoResult struct { + document IDocument +} + +func NewEchoResult(document IDocument, _ interface{}) IEchoResult { + return &echoResult{ + document: document, + } +} + +func (result echoResult) Document() IDocument { + return result.document +} + +//endregion + +func (manager *spiManager) Echo(document IDocument, editor Identity) (result IEchoResult, err error) { + defer func() { + if reason := recover(); reason != nil { + err = manager.Error(reason) + } + }() + + editor.Lock(ECHO_REQUEST) + defer editor.Unlock(ECHO_REQUEST) + + if result, err = commands.Echo(NewDispatcher(Conductor, editor), document); err != nil { + return nil, err + } else { + return result, nil + } +} diff --git a/greataped/components/core/spi_manager_test.go b/greataped/components/core/spi_manager_test.go new file mode 100644 index 0000000..e75beeb --- /dev/null +++ b/greataped/components/core/spi_manager_test.go @@ -0,0 +1,161 @@ +package core_test + +import ( + "testing" + + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/core" +) + +func TestSpiManager_GetName(test *testing.T) { + manager := Conductor.SpiManager() + + if manager.Name() != SPI_MANAGER { + test.Fail() + } +} + +func TestSpiManager_ResolveDependencies(test *testing.T) { + manager := Conductor.SpiManager() + + if err := manager.ResolveDependencies(); err != nil { + test.Fatal(err) + } +} + +func TestSpiManager_Load(test *testing.T) { + manager := Conductor.SpiManager() + + if err := manager.Load(); err != nil { + test.Fatal(err) + } +} + +func TestSpiManager_Reload(test *testing.T) { + manager := Conductor.SpiManager() + + if err := manager.Reload(); err != nil && err != ERROR_OPERATION_NOT_SUPPORTED { + test.Fatal(err) + } +} + +func TestSpiManager_Count(test *testing.T) { + manager := Conductor.SpiManager() + + _ = manager.Count() +} + +func TestSpiManager_Exists(test *testing.T) { + manager := Conductor.SpiManager() + + if manager.Exists(0) { + test.FailNow() + } +} + +func TestSpiManager_ListSpis(test *testing.T) { + manager := Conductor.SpiManager() + + _ = manager.ListSpis(0, 0, "", nil) +} + +func TestSpiManager_GetSpi(test *testing.T) { + manager := Conductor.SpiManager() + + if spi, err := manager.GetSpi(0, nil); err == nil { + _ = spi + test.FailNow() + } +} + +func TestSpiManager_AddSpi(test *testing.T) { + manager := Conductor.SpiManager() + + spi, err := manager.AddSpi(nil) + if err != nil { + test.Fatal(err) + } + + _ = spi +} + +func TestSpiManager_UpdateSpi(test *testing.T) { + manager := Conductor.SpiManager() + + spi, err := manager.UpdateSpi(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = spi +} + +func TestSpiManager_RemoveSpi(test *testing.T) { + manager := Conductor.SpiManager() + + spi, err := manager.RemoveSpi(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = spi +} + +func TestSpiManager_Find(test *testing.T) { + manager := Conductor.SpiManager() + + spi := manager.Find(0) + if spi == nil { + test.Fail() + } + + _ = spi +} + +func TestSpiManager_ForEach(test *testing.T) { + manager := Conductor.SpiManager() + + manager.ForEach(func(spi ISpi) { + _ = spi + }) +} + +func TestSpiManager_Filter(test *testing.T) { + manager := Conductor.SpiManager() + + spis := manager.Filter(func(spi ISpi) bool { + return false + }) + + if spis.IsNotEmpty() { + test.Fail() + } + + _ = spis +} + +func TestSpiManager_Map(test *testing.T) { + manager := Conductor.SpiManager() + + spis := manager.Map(func(spi ISpi) ISpi { + return spi + }) + + if spis.Count() != manager.Count() { + test.Fail() + } + + _ = spis +} + +func TestSpiManager_Echo(test *testing.T) { + manager := Conductor.SpiManager() + + result, err := manager.Echo(nil, nil) + if err != nil { + test.Fatal(err) + } + + _ = result +} diff --git a/greataped/components/core/system_component.go b/greataped/components/core/system_component.go new file mode 100644 index 0000000..a922d40 --- /dev/null +++ b/greataped/components/core/system_component.go @@ -0,0 +1,140 @@ +package core + +import ( + "fmt" + "regexp" + "sync" + "time" + + "github.com/google/uuid" + . "github.com/xeronith/diamante/contracts/email" + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/sms" + "github.com/xeronith/diamante/utility" + "github.com/xeronith/diamante/utility/concurrent" + "github.com/xeronith/diamante/utility/jwt" + "rail.town/infrastructure/providers/outbound/email" + "rail.town/infrastructure/providers/outbound/sms" +) + +type systemComponent struct { + sync.RWMutex + lastId int64 + logger ILogger + configuration IConfiguration + emailProvider IEmailProvider + smsProvider ISMSProvider +} + +func newSystemComponent(configuration IConfiguration, logger ILogger) systemComponent { + return systemComponent{ + logger: logger, + configuration: configuration, + emailProvider: email.NewProvider(logger), + smsProvider: sms.NewProvider(logger), + } +} + +func (component *systemComponent) IsTestEnvironment() bool { + return component.configuration.IsTestEnvironment() +} + +func (component *systemComponent) IsDevelopmentEnvironment() bool { + return component.configuration.IsDevelopmentEnvironment() +} + +func (component *systemComponent) IsStagingEnvironment() bool { + return component.configuration.IsStagingEnvironment() +} + +func (component *systemComponent) IsProductionEnvironment() bool { + return component.configuration.IsProductionEnvironment() +} + +func (component *systemComponent) UniqueId() int64 { + component.Lock() + defer component.Unlock() + + var id int64 + for { + id = time.Now().UnixNano() / 1000 + if id != component.lastId { + break + } + + time.Sleep(time.Microsecond) + } + + component.lastId = id + return id +} + +func (component *systemComponent) Logger() ILogger { + return component.logger +} + +func (component *systemComponent) Async(runnable func()) { + concurrent.NewAsyncTask(runnable).Run() +} + +// Utility + +func (component *systemComponent) UnixNano() int64 { + return time.Now().UnixNano() +} + +func (component *systemComponent) GenerateUUID() string { + return uuid.New().String() +} + +func (component *systemComponent) GenerateSalt() string { + return uuid.New().String() +} + +func (component *systemComponent) GenerateHash(value string, salt string) string { + return utility.GenerateHash(value, salt) +} + +func (component *systemComponent) GenerateJwtToken() string { + return jwt.Generate() +} + +func (component *systemComponent) VerifyJwtToken(token string) error { + _, err := jwt.Verify(token) + return err +} + +func (component *systemComponent) GenerateCode() string { + return utility.GenerateConfirmationCode() +} + +func (component *systemComponent) GenerateRSAKeyPair() (string, string, error) { + return utility.GenerateRSAKeyPair() +} + +func (component *systemComponent) Email(destination string, format string, args ...interface{}) { + component.Async(func() { + message := fmt.Sprintf(format, args...) + component.emailProvider.Send(destination, message) + }) +} + +func (component *systemComponent) SMS(destination string, format string, args ...interface{}) { + component.Async(func() { + message := fmt.Sprintf(format, args...) + component.smsProvider.Send(destination, message) + }) +} + +func (component *systemComponent) Format(format string, args ...interface{}) string { + return fmt.Sprintf(format, args...) +} + +func (component *systemComponent) Match(pattern, input string) (bool, error) { + return regexp.MatchString(pattern, input) +} + +func (component *systemComponent) Error(arg interface{}) error { + return fmt.Errorf("%v", arg) +} diff --git a/greataped/components/core/system_dispatcher.go b/greataped/components/core/system_dispatcher.go new file mode 100644 index 0000000..dd40249 --- /dev/null +++ b/greataped/components/core/system_dispatcher.go @@ -0,0 +1,308 @@ +package core + +import ( + "fmt" + "math/rand" + "net/http" + "sort" + "strings" + "time" + + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" + "github.com/xeronith/diamante/utility/search" + . "rail.town/infrastructure/components/contracts" +) + +//region IDispatcher Implementation + +type dispatcher struct { + cache IDispatcherCache + conductor IConductor + identity Identity + transaction ITransaction +} + +func NewDispatcher(conductor IConductor, identity Identity) IDispatcher { + return &dispatcher{ + cache: newDispatcherCache(conductor, identity), + conductor: conductor, + identity: identity, + } +} + +func (dispatcher *dispatcher) Logger() ILogger { + return dispatcher.conductor.Logger() +} + +func (dispatcher *dispatcher) Config() IConfiguration { + return dispatcher.conductor.Configuration() +} + +func (dispatcher *dispatcher) FQDN() string { + config := dispatcher.conductor.Configuration().GetServerConfiguration() + return config.GetFQDN() +} + +func (dispatcher *dispatcher) PublicUrl() string { + config := dispatcher.conductor.Configuration().GetServerConfiguration() + return fmt.Sprintf("%s://%s", config.GetProtocol(), config.GetFQDN()) +} + +func (dispatcher *dispatcher) Accelerator() IDispatcherCache { + return dispatcher.cache +} + +func (dispatcher *dispatcher) Atomic(action SystemAction) { + if err := dispatcher.conductor.Atomic(func(transaction ITransaction) error { + defer func() { + dispatcher.transaction = nil + }() + + dispatcher.transaction = transaction + return action() + }); err != nil { + panic(err.Error()) + } +} + +func (dispatcher *dispatcher) Schedule(id int64, spec string, callback func(IDispatcher, string)) error { + return dispatcher.conductor.Schedule(spec, func() { + if !dispatcher.SystemScheduleExists(id) { + dispatcher.AddSystemScheduleWithCustomId(id, true, "") + } + + systemSchedule := dispatcher.GetSystemSchedule(id) + if !systemSchedule.Enabled() { + return + } + + defer func() { + if reason := recover(); reason != nil { + dispatcher.conductor.Logger().Panic(fmt.Sprintf("JOB_SCHEDULER: %s", reason)) + } + }() + + callback(dispatcher, systemSchedule.Config()) + }) +} + +func (dispatcher *dispatcher) Transaction() ITransaction { + return dispatcher.transaction +} + +func (dispatcher *dispatcher) IdentityManager() IIdentityManager { + return dispatcher.conductor.IdentityManager() +} + +func (dispatcher *dispatcher) Identity() Identity { + return dispatcher.identity +} + +func (dispatcher *dispatcher) CurrentUser() IUser { + return dispatcher.GetUser(dispatcher.identity.Id()) +} + +func (dispatcher *dispatcher) SignOut() error { + return dispatcher.conductor.IdentityManager().SignOut(dispatcher.identity) +} + +func (dispatcher *dispatcher) NewDocument(id int64, content string) (IDocument, error) { + return NewDocument(id, content) +} + +func (dispatcher *dispatcher) NewSystemSchedule(id int64, enabled bool, config string) (ISystemSchedule, error) { + return NewSystemSchedule(id, enabled, config) +} + +func (dispatcher *dispatcher) NewIdentity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) (IIdentity, error) { + return NewIdentity(id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount) +} + +func (dispatcher *dispatcher) NewAccessControl(id int64, key uint64, value uint64) (IAccessControl, error) { + return NewAccessControl(id, key, value) +} + +func (dispatcher *dispatcher) NewRemoteActivity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) (IRemoteActivity, error) { + return NewRemoteActivity(id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp) +} + +func (dispatcher *dispatcher) NewCategoryType(id int64, description string) (ICategoryType, error) { + return NewCategoryType(id, description) +} + +func (dispatcher *dispatcher) NewCategory(id int64, categoryTypeId int64, categoryId int64, title string, description string) (ICategory, error) { + return NewCategory(id, categoryTypeId, categoryId, title, description) +} + +func (dispatcher *dispatcher) NewUser(id int64, github string) (IUser, error) { + return NewUser(id, github) +} + +func (dispatcher *dispatcher) NewSpi() (ISpi, error) { + return NewSpi() +} + +func (dispatcher *dispatcher) NewCustomError() (ICustomError, error) { + return NewCustomError() +} + +func (dispatcher *dispatcher) Assert(condition bool) IAssertionResult { + return &assertionResult{ + condition: condition, + } +} + +func (dispatcher *dispatcher) AssertNoError(err error) { + if err != nil { + panic(err.Error()) + } +} + +func (dispatcher *dispatcher) Ensure(errors ...error) { + for _, err := range errors { + if err != nil { + panic(err.Error()) + } + } +} + +func (dispatcher *dispatcher) AssertNull(x interface{}) IAssertionResult { + return &assertionResult{ + condition: x == nil, + } +} + +func (dispatcher *dispatcher) AssertNotNull(x interface{}) IAssertionResult { + return &assertionResult{ + condition: x != nil, + } +} + +func (dispatcher *dispatcher) AssertEmpty(x string) IAssertionResult { + return &assertionResult{ + condition: strings.TrimSpace(x) == "", + } +} + +func (dispatcher *dispatcher) AssertNotEmpty(x string) IAssertionResult { + return &assertionResult{ + condition: strings.TrimSpace(x) != "", + } +} + +func (dispatcher *dispatcher) Format(format string, args ...interface{}) string { + return fmt.Sprintf(format, args...) +} + +func (dispatcher *dispatcher) Sort(slice interface{}, less func(a, b int) bool) { + sort.Slice(slice, less) +} + +func (dispatcher *dispatcher) Search(input, criteria string) bool { + return search.MatchAny(input, criteria) +} + +func (dispatcher *dispatcher) Email(destination string, format string, args ...interface{}) { + dispatcher.conductor.IdentityManager().Email(destination, format, args...) +} + +func (dispatcher *dispatcher) SMS(destination string, format string, args ...interface{}) { + dispatcher.conductor.IdentityManager().SMS(destination, format, args...) +} + +func (dispatcher *dispatcher) GenerateTrackingNumber() uint32 { + rand.Seed(time.Now().UnixNano()) + return 100000 + uint32(rand.Intn(899999)) +} + +func (dispatcher *dispatcher) GenerateUUID() string { + return dispatcher.conductor.IdentityManager().GenerateUUID() +} + +func (dispatcher *dispatcher) GenerateSalt() string { + return dispatcher.conductor.IdentityManager().GenerateSalt() +} + +func (dispatcher *dispatcher) GenerateHash(value string, salt string) string { + return dispatcher.conductor.IdentityManager().GenerateHash(value, salt) +} + +func (dispatcher *dispatcher) GenerateJwtToken() string { + return dispatcher.conductor.IdentityManager().GenerateJwtToken() +} + +func (dispatcher *dispatcher) VerifyJwtToken(token string) error { + return dispatcher.conductor.IdentityManager().VerifyJwtToken(token) +} + +func (dispatcher *dispatcher) GenerateCode() string { + return dispatcher.conductor.IdentityManager().GenerateCode() +} + +func (dispatcher *dispatcher) GenerateRSAKeyPair() (string, string, error) { + return dispatcher.conductor.IdentityManager().GenerateRSAKeyPair() +} + +func (dispatcher *dispatcher) UnixNano() int64 { + return time.Now().UnixNano() +} + +func (dispatcher *dispatcher) Trim(input string) string { + return strings.TrimSpace(input) +} + +func (dispatcher *dispatcher) Contains(input, substr string) bool { + return strings.Contains(input, substr) +} + +func (dispatcher *dispatcher) IsEmpty(input string) bool { + return strings.TrimSpace(input) == "" +} + +func (dispatcher *dispatcher) IsNotEmpty(input string) bool { + return strings.TrimSpace(input) != "" +} + +func (dispatcher *dispatcher) IsSet(id int64) bool { + return id != 0 +} + +func (dispatcher *dispatcher) Join(elements []string, separator string) string { + return strings.Join(elements, separator) +} + +func (dispatcher *dispatcher) IsTestEnvironment() bool { + return dispatcher.conductor.IdentityManager().IsTestEnvironment() +} + +func (dispatcher *dispatcher) IsDevelopmentEnvironment() bool { + return dispatcher.conductor.IdentityManager().IsDevelopmentEnvironment() +} + +func (dispatcher *dispatcher) IsStagingEnvironment() bool { + return dispatcher.conductor.IdentityManager().IsStagingEnvironment() +} + +func (dispatcher *dispatcher) IsProductionEnvironment() bool { + return dispatcher.conductor.IdentityManager().IsProductionEnvironment() +} + +func (dispatcher *dispatcher) GetActivityStream(url string, data []byte, output interface{}) error { + return dispatcher.conductor.RequestActivityStream(http.MethodGet, url, "", "", data, output) +} + +func (dispatcher *dispatcher) PostActivityStream(url string, data []byte, output interface{}) error { + return dispatcher.conductor.RequestActivityStream(http.MethodPost, url, "", "", data, output) +} + +func (dispatcher *dispatcher) GetActivityStreamSigned(url, keyId, privateKey string, data []byte, output interface{}) error { + return dispatcher.conductor.RequestActivityStream(http.MethodGet, url, keyId, privateKey, data, output) +} + +func (dispatcher *dispatcher) PostActivityStreamSigned(url, keyId, privateKey string, data []byte, output interface{}) error { + return dispatcher.conductor.RequestActivityStream(http.MethodPost, url, keyId, privateKey, data, output) +} + +//endregion diff --git a/greataped/components/core/system_dispatcher_cache.go b/greataped/components/core/system_dispatcher_cache.go new file mode 100644 index 0000000..aa85662 --- /dev/null +++ b/greataped/components/core/system_dispatcher_cache.go @@ -0,0 +1,165 @@ +package core + +import ( + "sync" + "time" + + . "github.com/xeronith/diamante/contracts/security" + . "rail.town/infrastructure/components/contracts" +) + +//region IDispatcherCache Implementation + +var EPOCH = time.Date(1970, time.January, 0, 0, 0, 0, 0, time.UTC) + +type dispatcherCache struct { + conductor IConductor + identity Identity + timeout time.Duration + + categoriesByCategoryTypeCacheInstance *categoriesByCategoryTypeCache + categoriesByCategoryCacheInstance *categoriesByCategoryCache +} + +func newDispatcherCache(conductor IConductor, identity Identity) IDispatcherCache { + instance := &dispatcherCache{ + conductor: conductor, + identity: identity, + timeout: 30 * time.Minute, + + categoriesByCategoryTypeCacheInstance: newCategoriesByCategoryTypeCache(), + categoriesByCategoryCacheInstance: newCategoriesByCategoryCache(), + } + + // Categories + instance.conductor.CategoryManager().OnCacheChanged(func() { + instance.categoriesByCategoryTypeCacheInstance.Invalidate() + instance.categoriesByCategoryCacheInstance.Invalidate() + }) + + return instance +} + +// CategoriesByCategoryType +// ------------------------------------------------------------ + +type categoriesByCategoryTypeCache struct { + sync.RWMutex + items map[int64]ICategoryCollection + lastUpdate time.Time +} + +func newCategoriesByCategoryTypeCache() *categoriesByCategoryTypeCache { + return &categoriesByCategoryTypeCache{ + items: make(map[int64]ICategoryCollection), + lastUpdate: EPOCH, + } +} + +func (cache *categoriesByCategoryTypeCache) Invalidate() { + cache.Lock() + defer cache.Unlock() + + cache.lastUpdate = EPOCH +} + +func (cache *dispatcherCache) ListCategoriesByCategoryType(categoryType ICategoryType) ICategoryCollection { + return cache.ListCategoriesByCategoryTypeId(categoryType.Id()) +} + +func (cache *dispatcherCache) ListCategoriesByCategoryTypeId(categoryTypeId int64) ICategoryCollection { + instance := cache.categoriesByCategoryTypeCacheInstance + + func() { + instance.Lock() + defer instance.Unlock() + + if time.Since(instance.lastUpdate) > cache.timeout { + cache.conductor.CategoryManager().ForEach(func(category ICategory) { + instance.items[category.CategoryTypeId()].Append(category) + }) + + instance.lastUpdate = time.Now() + } + }() + + instance.RLock() + defer instance.RUnlock() + + if item, exists := instance.items[categoryTypeId]; exists { + return item + } + + return NewCategories() +} + +func (cache *dispatcherCache) ForEachCategoryByCategoryType(categoryType ICategoryType, iterator CategoryIterator) { + cache.ForEachCategoryByCategoryTypeId(categoryType.Id(), iterator) +} + +func (cache *dispatcherCache) ForEachCategoryByCategoryTypeId(categoryTypeId int64, iterator CategoryIterator) { + cache.ListCategoriesByCategoryTypeId(categoryTypeId).ForEach(iterator) +} + +// CategoriesByCategory +// ------------------------------------------------------------ + +type categoriesByCategoryCache struct { + sync.RWMutex + items map[int64]ICategoryCollection + lastUpdate time.Time +} + +func newCategoriesByCategoryCache() *categoriesByCategoryCache { + return &categoriesByCategoryCache{ + items: make(map[int64]ICategoryCollection), + lastUpdate: EPOCH, + } +} + +func (cache *categoriesByCategoryCache) Invalidate() { + cache.Lock() + defer cache.Unlock() + + cache.lastUpdate = EPOCH +} + +func (cache *dispatcherCache) ListCategoriesByCategory(category ICategory) ICategoryCollection { + return cache.ListCategoriesByCategoryId(category.Id()) +} + +func (cache *dispatcherCache) ListCategoriesByCategoryId(categoryId int64) ICategoryCollection { + instance := cache.categoriesByCategoryCacheInstance + + func() { + instance.Lock() + defer instance.Unlock() + + if time.Since(instance.lastUpdate) > cache.timeout { + cache.conductor.CategoryManager().ForEach(func(category ICategory) { + instance.items[category.CategoryId()].Append(category) + }) + + instance.lastUpdate = time.Now() + } + }() + + instance.RLock() + defer instance.RUnlock() + + if item, exists := instance.items[categoryId]; exists { + return item + } + + return NewCategories() +} + +func (cache *dispatcherCache) ForEachCategoryByCategory(category ICategory, iterator CategoryIterator) { + cache.ForEachCategoryByCategoryId(category.Id(), iterator) +} + +func (cache *dispatcherCache) ForEachCategoryByCategoryId(categoryId int64, iterator CategoryIterator) { + cache.ListCategoriesByCategoryId(categoryId).ForEach(iterator) +} + +//endregion diff --git a/greataped/components/core/system_results.go b/greataped/components/core/system_results.go new file mode 100644 index 0000000..ce5eb6a --- /dev/null +++ b/greataped/components/core/system_results.go @@ -0,0 +1,15 @@ +package core + +import . "rail.town/infrastructure/components/contracts" + +//region IDispatcher Implementation + +func (dispatcher *dispatcher) NewEchoResult(document IDocument) IEchoResult { + return NewEchoResult(document, nil) +} + +func (dispatcher *dispatcher) NewResolveErrorResult() IResolveErrorResult { + return NewResolveErrorResult(nil) +} + +//endregion diff --git a/greataped/components/core/system_schedule.go b/greataped/components/core/system_schedule.go new file mode 100644 index 0000000..2aec8a6 --- /dev/null +++ b/greataped/components/core/system_schedule.go @@ -0,0 +1,306 @@ +package core + +import ( + "fmt" + + . "github.com/xeronith/diamante/contracts/security" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + "rail.town/infrastructure/components/model/repository" +) + +type systemSchedule struct { + object + enabled bool + config string +} + +// noinspection GoUnusedExportedFunction +func InitializeSystemSchedule() { + _ = ENABLE_SECURITY + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + _ = repository.Initialize +} + +func NewSystemSchedule(id int64, enabled bool, config string) (ISystemSchedule, error) { + instance := &systemSchedule{ + object: object{ + id: id, + }, + enabled: enabled, + config: config, + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func NewSystemScheduleFromEntity(entity ISystemScheduleEntity) (ISystemSchedule, error) { + instance := &systemSchedule{ + object: object{ + id: entity.Id(), + }, + enabled: entity.Enabled(), + config: entity.Config(), + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func (systemSchedule *systemSchedule) Enabled() bool { + return systemSchedule.enabled +} + +func (systemSchedule *systemSchedule) UpdateEnabled(enabled bool, editor Identity) { + if err := repository.SystemSchedules.UpdateEnabled(systemSchedule.id, enabled, editor.Id()); err != nil { + panic(err.Error()) + } + + systemSchedule.enabled = enabled +} + +func (systemSchedule *systemSchedule) UpdateEnabledAtomic(transaction ITransaction, enabled bool, editor Identity) { + transaction.OnCommit(func() { + systemSchedule.enabled = enabled + }) + + if err := repository.SystemSchedules.UpdateEnabledAtomic(transaction, systemSchedule.id, enabled, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (systemSchedule *systemSchedule) Config() string { + return systemSchedule.config +} + +func (systemSchedule *systemSchedule) UpdateConfig(config string, editor Identity) { + if err := repository.SystemSchedules.UpdateConfig(systemSchedule.id, config, editor.Id()); err != nil { + panic(err.Error()) + } + + systemSchedule.config = config +} + +func (systemSchedule *systemSchedule) UpdateConfigAtomic(transaction ITransaction, config string, editor Identity) { + transaction.OnCommit(func() { + systemSchedule.config = config + }) + + if err := repository.SystemSchedules.UpdateConfigAtomic(transaction, systemSchedule.id, config, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (systemSchedule *systemSchedule) Validate() error { + return nil +} + +func (systemSchedule *systemSchedule) String() string { + return fmt.Sprintf("SystemSchedule (Id: %d, Enabled: %v, Config: %v)", systemSchedule.Id(), systemSchedule.Enabled(), systemSchedule.Config()) +} + +//------------------------------------------------------------------------------ + +type systemSchedules struct { + collection SystemSchedules +} + +// NewSystemSchedules creates an empty collection of 'System Schedule' which is not thread-safe. +func NewSystemSchedules() ISystemScheduleCollection { + return &systemSchedules{ + collection: make(SystemSchedules, 0), + } +} + +func (systemSchedules *systemSchedules) Count() int { + return len(systemSchedules.collection) +} + +func (systemSchedules *systemSchedules) IsEmpty() bool { + return len(systemSchedules.collection) == 0 +} + +func (systemSchedules *systemSchedules) IsNotEmpty() bool { + return len(systemSchedules.collection) > 0 +} + +func (systemSchedules *systemSchedules) HasExactlyOneItem() bool { + return len(systemSchedules.collection) == 1 +} + +func (systemSchedules *systemSchedules) HasAtLeastOneItem() bool { + return len(systemSchedules.collection) >= 1 +} + +func (systemSchedules *systemSchedules) First() ISystemSchedule { + return systemSchedules.collection[0] +} + +func (systemSchedules *systemSchedules) Append(systemSchedule ISystemSchedule) { + systemSchedules.collection = append(systemSchedules.collection, systemSchedule) +} + +func (systemSchedules *systemSchedules) ForEach(iterator SystemScheduleIterator) { + if iterator == nil { + return + } + + for _, value := range systemSchedules.collection { + iterator(value) + } +} + +func (systemSchedules *systemSchedules) Array() SystemSchedules { + return systemSchedules.collection +} + +//------------------------------------------------------------------------------ + +func (dispatcher *dispatcher) SystemScheduleExists(id int64) bool { + return dispatcher.conductor.SystemScheduleManager().Exists(id) +} + +func (dispatcher *dispatcher) SystemScheduleExistsWhich(condition SystemScheduleCondition) bool { + return dispatcher.conductor.SystemScheduleManager().ExistsWhich(condition) +} + +func (dispatcher *dispatcher) ListSystemSchedules() ISystemScheduleCollection { + return dispatcher.conductor.SystemScheduleManager().ListSystemSchedules(0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachSystemSchedule(iterator SystemScheduleIterator) { + dispatcher.conductor.SystemScheduleManager().ForEach(iterator) +} + +func (dispatcher *dispatcher) FilterSystemSchedules(predicate SystemScheduleFilterPredicate) ISystemScheduleCollection { + return dispatcher.conductor.SystemScheduleManager().Filter(predicate) +} + +func (dispatcher *dispatcher) MapSystemSchedules(predicate SystemScheduleMapPredicate) ISystemScheduleCollection { + return dispatcher.conductor.SystemScheduleManager().Map(predicate) +} + +func (dispatcher *dispatcher) GetSystemSchedule(id int64) ISystemSchedule { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().GetSystemSchedule(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } +} + +func (dispatcher *dispatcher) AddSystemSchedule(enabled bool, config string) ISystemSchedule { + transaction := dispatcher.transaction + if transaction != nil { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().AddSystemScheduleAtomic(transaction, enabled, config, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } else { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().AddSystemSchedule(enabled, config, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } +} + +func (dispatcher *dispatcher) AddSystemScheduleWithCustomId(id int64, enabled bool, config string) ISystemSchedule { + transaction := dispatcher.transaction + if transaction != nil { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().AddSystemScheduleWithCustomIdAtomic(id, transaction, enabled, config, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } else { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().AddSystemScheduleWithCustomId(id, enabled, config, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } +} + +func (dispatcher *dispatcher) LogSystemSchedule(enabled bool, config string, source string, payload string) { + dispatcher.conductor.SystemScheduleManager().Log(enabled, config, source, dispatcher.identity, payload) +} + +func (dispatcher *dispatcher) UpdateSystemSchedule(id int64, enabled bool, config string) ISystemSchedule { + transaction := dispatcher.transaction + if transaction != nil { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().UpdateSystemScheduleAtomic(transaction, id, enabled, config, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } else { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().UpdateSystemSchedule(id, enabled, config, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } +} + +// noinspection GoUnusedParameter +func (dispatcher *dispatcher) UpdateSystemScheduleObject(object IObject, systemSchedule ISystemSchedule) ISystemSchedule { + transaction := dispatcher.transaction + if transaction != nil { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().UpdateSystemScheduleAtomic(transaction, object.Id(), systemSchedule.Enabled(), systemSchedule.Config(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } else { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().UpdateSystemSchedule(object.Id(), systemSchedule.Enabled(), systemSchedule.Config(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } +} + +func (dispatcher *dispatcher) AddOrUpdateSystemScheduleObject(object IObject, systemSchedule ISystemSchedule) ISystemSchedule { + transaction := dispatcher.transaction + if transaction != nil { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().AddOrUpdateSystemScheduleObjectAtomic(transaction, object.Id(), systemSchedule, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } else { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().AddOrUpdateSystemScheduleObject(object.Id(), systemSchedule, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } +} + +func (dispatcher *dispatcher) RemoveSystemSchedule(id int64) ISystemSchedule { + transaction := dispatcher.transaction + if transaction != nil { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().RemoveSystemScheduleAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } else { + if systemSchedule, err := dispatcher.conductor.SystemScheduleManager().RemoveSystemSchedule(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return systemSchedule + } + } +} diff --git a/greataped/components/core/system_schedule_manager.go b/greataped/components/core/system_schedule_manager.go new file mode 100644 index 0000000..219a365 --- /dev/null +++ b/greataped/components/core/system_schedule_manager.go @@ -0,0 +1,266 @@ +package core + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/system" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" + "rail.town/infrastructure/components/model/repository" +) + +// noinspection GoSnakeCaseUsage +const SYSTEM_SCHEDULE_MANAGER = "SystemScheduleManager" + +type systemScheduleManager struct { + systemComponent + cache ICache +} + +func newSystemScheduleManager(configuration IConfiguration, logger ILogger, dependencies ...ISystemComponent) ISystemScheduleManager { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + + manager := &systemScheduleManager{ + systemComponent: newSystemComponent(configuration, logger), + cache: NewCache(), + } + + if err := manager.ResolveDependencies(dependencies...); err != nil { + return nil + } + + return manager +} + +func (manager *systemScheduleManager) Name() string { + return SYSTEM_SCHEDULE_MANAGER +} + +func (manager *systemScheduleManager) ResolveDependencies(_ ...ISystemComponent) error { + return nil +} + +func (manager *systemScheduleManager) Load() error { + systemScheduleEntities, err := repository.SystemSchedules.FetchAll() + if err != nil { + return err + } + + systemSchedules := make(SystemObjectCache) + for _, systemScheduleEntity := range systemScheduleEntities { + if systemSchedule, err := NewSystemScheduleFromEntity(systemScheduleEntity); err == nil { + systemSchedules[systemSchedule.Id()] = systemSchedule + } else { + return err + } + } + + manager.cache.Load(systemSchedules) + return nil +} + +func (manager *systemScheduleManager) Reload() error { + return manager.Load() +} + +func (manager *systemScheduleManager) OnCacheChanged(callback SystemScheduleCacheCallback) { + manager.cache.OnChanged(callback) +} + +func (manager *systemScheduleManager) Count() int { + return manager.cache.Size() +} + +func (manager *systemScheduleManager) Exists(id int64) bool { + return manager.Find(id) != nil +} + +func (manager *systemScheduleManager) ExistsWhich(condition SystemScheduleCondition) bool { + var systemSchedules SystemSchedules + manager.ForEach(func(systemSchedule ISystemSchedule) { + if condition(systemSchedule) { + systemSchedules = append(systemSchedules, systemSchedule) + } + }) + + return len(systemSchedules) > 0 +} + +func (manager *systemScheduleManager) ListSystemSchedules(_ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) ISystemScheduleCollection { + return manager.Filter(SystemSchedulePassThroughFilter) +} + +func (manager *systemScheduleManager) GetSystemSchedule(id int64, _ Identity) (ISystemSchedule, error) { + if systemSchedule := manager.Find(id); systemSchedule == nil { + return nil, ERROR_SYSTEM_SCHEDULE_NOT_FOUND + } else { + return systemSchedule, nil + } +} + +func (manager *systemScheduleManager) AddSystemSchedule(enabled bool, config string, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(manager.UniqueId(), enabled, config) + return manager.Apply(systemScheduleEntity, repository.SystemSchedules.Add, manager.cache.Put, editor) +} + +func (manager *systemScheduleManager) AddSystemScheduleWithCustomId(id int64, enabled bool, config string, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(id, enabled, config) + return manager.Apply(systemScheduleEntity, repository.SystemSchedules.Add, manager.cache.Put, editor) +} + +func (manager *systemScheduleManager) AddSystemScheduleObject(systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(manager.UniqueId(), systemSchedule.Enabled(), systemSchedule.Config()) + return manager.Apply(systemScheduleEntity, repository.SystemSchedules.Add, manager.cache.Put, editor) +} + +func (manager *systemScheduleManager) AddSystemScheduleAtomic(transaction ITransaction, enabled bool, config string, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(manager.UniqueId(), enabled, config) + return manager.ApplyAtomic(transaction, systemScheduleEntity, repository.SystemSchedules.AddAtomic, manager.cache.Put, editor) +} + +func (manager *systemScheduleManager) AddSystemScheduleWithCustomIdAtomic(id int64, transaction ITransaction, enabled bool, config string, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(id, enabled, config) + return manager.ApplyAtomic(transaction, systemScheduleEntity, repository.SystemSchedules.AddAtomic, manager.cache.Put, editor) +} + +func (manager *systemScheduleManager) AddSystemScheduleObjectAtomic(transaction ITransaction, systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(manager.UniqueId(), systemSchedule.Enabled(), systemSchedule.Config()) + return manager.ApplyAtomic(transaction, systemScheduleEntity, repository.SystemSchedules.AddAtomic, manager.cache.Put, editor) +} + +func (manager *systemScheduleManager) Log(enabled bool, config string, source string, editor Identity, payload string) { + systemSchedulePipeEntity := NewSystemSchedulePipeEntity(manager.UniqueId(), enabled, config, source, editor.Id(), payload) + repository.Pipe.Insert(systemSchedulePipeEntity) + + systemSchedule, err := NewSystemScheduleFromEntity(systemSchedulePipeEntity) + if err != nil { + manager.Logger().Error(err) + } else { + manager.cache.Put(systemSchedule.Id(), systemSchedule) + } +} + +func (manager *systemScheduleManager) UpdateSystemSchedule(id int64, enabled bool, config string, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(id, enabled, config) + return manager.Apply(systemScheduleEntity, repository.SystemSchedules.Update, manager.cache.Put, editor) +} + +func (manager *systemScheduleManager) UpdateSystemScheduleObject(id int64, systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(id, systemSchedule.Enabled(), systemSchedule.Config()) + return manager.Apply(systemScheduleEntity, repository.SystemSchedules.Update, manager.cache.Put, editor) +} + +func (manager *systemScheduleManager) UpdateSystemScheduleAtomic(transaction ITransaction, id int64, enabled bool, config string, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(id, enabled, config) + return manager.ApplyAtomic(transaction, systemScheduleEntity, repository.SystemSchedules.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *systemScheduleManager) UpdateSystemScheduleObjectAtomic(transaction ITransaction, id int64, systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(id, systemSchedule.Enabled(), systemSchedule.Config()) + return manager.ApplyAtomic(transaction, systemScheduleEntity, repository.SystemSchedules.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *systemScheduleManager) AddOrUpdateSystemScheduleObject(id int64, systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) { + if manager.Exists(id) { + return manager.UpdateSystemScheduleObject(id, systemSchedule, editor) + } else { + return manager.AddSystemScheduleObject(systemSchedule, editor) + } +} + +func (manager *systemScheduleManager) AddOrUpdateSystemScheduleObjectAtomic(transaction ITransaction, id int64, systemSchedule ISystemSchedule, editor Identity) (ISystemSchedule, error) { + if manager.Exists(id) { + return manager.UpdateSystemScheduleObjectAtomic(transaction, id, systemSchedule, editor) + } else { + return manager.AddSystemScheduleObjectAtomic(transaction, systemSchedule, editor) + } +} + +func (manager *systemScheduleManager) RemoveSystemSchedule(id int64, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(id, false, "") + return manager.Apply(systemScheduleEntity, repository.SystemSchedules.Remove, manager.cache.Remove, editor) +} + +func (manager *systemScheduleManager) RemoveSystemScheduleAtomic(transaction ITransaction, id int64, editor Identity) (ISystemSchedule, error) { + systemScheduleEntity := NewSystemScheduleEntity(id, false, "") + return manager.ApplyAtomic(transaction, systemScheduleEntity, repository.SystemSchedules.RemoveAtomic, manager.cache.Remove, editor) +} + +func (manager *systemScheduleManager) Apply(systemScheduleEntity ISystemScheduleEntity, repositoryHandler func(ISystemScheduleEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (ISystemSchedule, error) { + result, err := NewSystemScheduleFromEntity(systemScheduleEntity) + if err != nil { + return nil, err + } + + if err := repositoryHandler(systemScheduleEntity, editor.Id()); err != nil { + return nil, err + } + + cacheHandler(result.Id(), result) + return result, nil +} + +func (manager *systemScheduleManager) ApplyAtomic(transaction ITransaction, systemScheduleEntity ISystemScheduleEntity, repositoryHandler func(IRepositoryTransaction, ISystemScheduleEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (ISystemSchedule, error) { + result, err := NewSystemScheduleFromEntity(systemScheduleEntity) + if err != nil { + return nil, err + } + + transaction.OnCommit(func() { + cacheHandler(result.Id(), result) + }) + + if err := repositoryHandler(transaction, systemScheduleEntity, editor.Id()); err != nil { + return nil, err + } + + return result, nil +} + +func (manager *systemScheduleManager) Find(id int64) ISystemSchedule { + if object, exists := manager.cache.Get(id); !exists { + return nil + } else { + return object.(ISystemSchedule) + } +} + +func (manager *systemScheduleManager) ForEach(iterator SystemScheduleIterator) { + manager.cache.ForEachValue(func(object ISystemObject) { + iterator(object.(ISystemSchedule)) + }) +} + +func (manager *systemScheduleManager) Filter(predicate SystemScheduleFilterPredicate) ISystemScheduleCollection { + systemSchedules := NewSystemSchedules() + if predicate == nil { + return systemSchedules + } + + manager.ForEach(func(systemSchedule ISystemSchedule) { + if predicate(systemSchedule) { + systemSchedules.Append(systemSchedule) + } + }) + + return systemSchedules +} + +func (manager *systemScheduleManager) Map(predicate SystemScheduleMapPredicate) ISystemScheduleCollection { + systemSchedules := NewSystemSchedules() + if predicate == nil { + return systemSchedules + } + + manager.ForEach(func(systemSchedule ISystemSchedule) { + systemSchedules.Append(predicate(systemSchedule)) + }) + + return systemSchedules +} diff --git a/greataped/components/core/system_schedule_manager_test.go b/greataped/components/core/system_schedule_manager_test.go new file mode 100644 index 0000000..c705b65 --- /dev/null +++ b/greataped/components/core/system_schedule_manager_test.go @@ -0,0 +1,150 @@ +package core_test + +import ( + "testing" + + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/core" +) + +func TestSystemScheduleManager_GetName(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + if manager.Name() != SYSTEM_SCHEDULE_MANAGER { + test.Fail() + } +} + +func TestSystemScheduleManager_ResolveDependencies(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + if err := manager.ResolveDependencies(); err != nil { + test.Fatal(err) + } +} + +func TestSystemScheduleManager_Load(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + if err := manager.Load(); err != nil { + test.Fatal(err) + } +} + +func TestSystemScheduleManager_Reload(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + if err := manager.Reload(); err != nil && err != ERROR_OPERATION_NOT_SUPPORTED { + test.Fatal(err) + } +} + +func TestSystemScheduleManager_Count(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + _ = manager.Count() +} + +func TestSystemScheduleManager_Exists(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + if manager.Exists(0) { + test.FailNow() + } +} + +func TestSystemScheduleManager_ListSystemSchedules(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + _ = manager.ListSystemSchedules(0, 0, "", nil) +} + +func TestSystemScheduleManager_GetSystemSchedule(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + if systemSchedule, err := manager.GetSystemSchedule(0, nil); err == nil { + _ = systemSchedule + test.FailNow() + } +} + +func TestSystemScheduleManager_AddSystemSchedule(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + systemSchedule, err := manager.AddSystemSchedule(true, "config", nil) + if err != nil { + test.Fatal(err) + } + + _ = systemSchedule +} + +func TestSystemScheduleManager_UpdateSystemSchedule(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + systemSchedule, err := manager.UpdateSystemSchedule(0, true, "config", nil) + if err != nil { + test.Fatal(err) + } + + _ = systemSchedule +} + +func TestSystemScheduleManager_RemoveSystemSchedule(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + systemSchedule, err := manager.RemoveSystemSchedule(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = systemSchedule +} + +func TestSystemScheduleManager_Find(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + systemSchedule := manager.Find(0) + if systemSchedule == nil { + test.Fail() + } + + _ = systemSchedule +} + +func TestSystemScheduleManager_ForEach(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + manager.ForEach(func(systemSchedule ISystemSchedule) { + _ = systemSchedule + }) +} + +func TestSystemScheduleManager_Filter(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + systemSchedules := manager.Filter(func(systemSchedule ISystemSchedule) bool { + return systemSchedule.Id() < 0 + }) + + if systemSchedules.IsNotEmpty() { + test.Fail() + } + + _ = systemSchedules +} + +func TestSystemScheduleManager_Map(test *testing.T) { + manager := Conductor.SystemScheduleManager() + + systemSchedules := manager.Map(func(systemSchedule ISystemSchedule) ISystemSchedule { + return systemSchedule + }) + + if systemSchedules.Count() != manager.Count() { + test.Fail() + } + + _ = systemSchedules +} diff --git a/greataped/components/core/user.go b/greataped/components/core/user.go new file mode 100644 index 0000000..2ad5b24 --- /dev/null +++ b/greataped/components/core/user.go @@ -0,0 +1,281 @@ +package core + +import ( + "fmt" + + . "github.com/xeronith/diamante/contracts/security" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + "rail.town/infrastructure/components/model/repository" +) + +type user struct { + object + github string +} + +// noinspection GoUnusedExportedFunction +func InitializeUser() { + _ = ENABLE_SECURITY + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + _ = repository.Initialize +} + +func NewUser(id int64, github string) (IUser, error) { + instance := &user{ + object: object{ + id: id, + }, + github: github, + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func NewUserFromEntity(entity IUserEntity) (IUser, error) { + instance := &user{ + object: object{ + id: entity.Id(), + }, + github: entity.Github(), + } + + if err := instance.Validate(); err != nil { + return nil, err + } + + return instance, nil +} + +func (user *user) Github() string { + return user.github +} + +func (user *user) UpdateGithub(github string, editor Identity) { + if err := repository.Users.UpdateGithub(user.id, github, editor.Id()); err != nil { + panic(err.Error()) + } + + user.github = github +} + +func (user *user) UpdateGithubAtomic(transaction ITransaction, github string, editor Identity) { + transaction.OnCommit(func() { + user.github = github + }) + + if err := repository.Users.UpdateGithubAtomic(transaction, user.id, github, editor.Id()); err != nil { + panic(err.Error()) + } +} + +func (user *user) Validate() error { + return nil +} + +func (user *user) String() string { + return fmt.Sprintf("User (Id: %d, Github: %v)", user.Id(), user.Github()) +} + +//------------------------------------------------------------------------------ + +type users struct { + collection Users +} + +// NewUsers creates an empty collection of 'User' which is not thread-safe. +func NewUsers() IUserCollection { + return &users{ + collection: make(Users, 0), + } +} + +func (users *users) Count() int { + return len(users.collection) +} + +func (users *users) IsEmpty() bool { + return len(users.collection) == 0 +} + +func (users *users) IsNotEmpty() bool { + return len(users.collection) > 0 +} + +func (users *users) HasExactlyOneItem() bool { + return len(users.collection) == 1 +} + +func (users *users) HasAtLeastOneItem() bool { + return len(users.collection) >= 1 +} + +func (users *users) First() IUser { + return users.collection[0] +} + +func (users *users) Append(user IUser) { + users.collection = append(users.collection, user) +} + +func (users *users) ForEach(iterator UserIterator) { + if iterator == nil { + return + } + + for _, value := range users.collection { + iterator(value) + } +} + +func (users *users) Array() Users { + return users.collection +} + +//------------------------------------------------------------------------------ + +func (dispatcher *dispatcher) UserExists(id int64) bool { + return dispatcher.conductor.UserManager().Exists(id) +} + +func (dispatcher *dispatcher) UserExistsWhich(condition UserCondition) bool { + return dispatcher.conductor.UserManager().ExistsWhich(condition) +} + +func (dispatcher *dispatcher) ListUsers() IUserCollection { + return dispatcher.conductor.UserManager().ListUsers(0, 0, "", dispatcher.identity) +} + +func (dispatcher *dispatcher) ForEachUser(iterator UserIterator) { + dispatcher.conductor.UserManager().ForEach(iterator) +} + +func (dispatcher *dispatcher) FilterUsers(predicate UserFilterPredicate) IUserCollection { + return dispatcher.conductor.UserManager().Filter(predicate) +} + +func (dispatcher *dispatcher) MapUsers(predicate UserMapPredicate) IUserCollection { + return dispatcher.conductor.UserManager().Map(predicate) +} + +func (dispatcher *dispatcher) GetUser(id int64) IUser { + if user, err := dispatcher.conductor.UserManager().GetUser(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } +} + +func (dispatcher *dispatcher) AddUser(identityId int64, github string) IUser { + transaction := dispatcher.transaction + if transaction != nil { + if user, err := dispatcher.conductor.UserManager().AddUserAtomic(transaction, identityId, github, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } else { + if user, err := dispatcher.conductor.UserManager().AddUser(identityId, github, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } +} + +func (dispatcher *dispatcher) AddUserObject(identity IIdentity, user IUser) IUser { + transaction := dispatcher.transaction + if transaction != nil { + if user, err := dispatcher.conductor.UserManager().AddUserObjectAtomic(transaction, identity.Id(), user, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } else { + if user, err := dispatcher.conductor.UserManager().AddUserObject(identity.Id(), user, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } +} + +func (dispatcher *dispatcher) LogUser(identityId int64, github string, source string, payload string) { + dispatcher.conductor.UserManager().Log(identityId, github, source, dispatcher.identity, payload) +} + +func (dispatcher *dispatcher) UpdateUser(id int64, github string) IUser { + transaction := dispatcher.transaction + if transaction != nil { + if user, err := dispatcher.conductor.UserManager().UpdateUserAtomic(transaction, id, github, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } else { + if user, err := dispatcher.conductor.UserManager().UpdateUser(id, github, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } +} + +// noinspection GoUnusedParameter +func (dispatcher *dispatcher) UpdateUserObject(object IObject, user IUser) IUser { + transaction := dispatcher.transaction + if transaction != nil { + if user, err := dispatcher.conductor.UserManager().UpdateUserAtomic(transaction, object.Id(), user.Github(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } else { + if user, err := dispatcher.conductor.UserManager().UpdateUser(object.Id(), user.Github(), dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } +} + +func (dispatcher *dispatcher) AddOrUpdateUserObject(object IObject, user IUser) IUser { + transaction := dispatcher.transaction + if transaction != nil { + if user, err := dispatcher.conductor.UserManager().AddOrUpdateUserObjectAtomic(transaction, object.Id(), user, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } else { + if user, err := dispatcher.conductor.UserManager().AddOrUpdateUserObject(object.Id(), user, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } +} + +func (dispatcher *dispatcher) RemoveUser(id int64) IUser { + transaction := dispatcher.transaction + if transaction != nil { + if user, err := dispatcher.conductor.UserManager().RemoveUserAtomic(transaction, id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } else { + if user, err := dispatcher.conductor.UserManager().RemoveUser(id, dispatcher.identity); err != nil { + panic(err.Error()) + } else { + return user + } + } +} diff --git a/greataped/components/core/user_manager.go b/greataped/components/core/user_manager.go new file mode 100644 index 0000000..654e513 --- /dev/null +++ b/greataped/components/core/user_manager.go @@ -0,0 +1,256 @@ +package core + +import ( + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/security" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/contracts/system" + . "github.com/xeronith/diamante/system" + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" + "rail.town/infrastructure/components/model/repository" +) + +// noinspection GoSnakeCaseUsage +const USER_MANAGER = "UserManager" + +type userManager struct { + systemComponent + cache ICache +} + +func newUserManager(configuration IConfiguration, logger ILogger, dependencies ...ISystemComponent) IUserManager { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize + + manager := &userManager{ + systemComponent: newSystemComponent(configuration, logger), + cache: NewCache(), + } + + if err := manager.ResolveDependencies(dependencies...); err != nil { + return nil + } + + return manager +} + +func (manager *userManager) Name() string { + return USER_MANAGER +} + +func (manager *userManager) ResolveDependencies(_ ...ISystemComponent) error { + return nil +} + +func (manager *userManager) Load() error { + userEntities, err := repository.Users.FetchAll() + if err != nil { + return err + } + + users := make(SystemObjectCache) + for _, userEntity := range userEntities { + if user, err := NewUserFromEntity(userEntity); err == nil { + users[user.Id()] = user + } else { + return err + } + } + + manager.cache.Load(users) + return nil +} + +func (manager *userManager) Reload() error { + return manager.Load() +} + +func (manager *userManager) OnCacheChanged(callback UserCacheCallback) { + manager.cache.OnChanged(callback) +} + +func (manager *userManager) Count() int { + return manager.cache.Size() +} + +func (manager *userManager) Exists(id int64) bool { + return manager.Find(id) != nil +} + +func (manager *userManager) ExistsWhich(condition UserCondition) bool { + var users Users + manager.ForEach(func(user IUser) { + if condition(user) { + users = append(users, user) + } + }) + + return len(users) > 0 +} + +func (manager *userManager) ListUsers(_ /* pageIndex */ uint32, _ /* pageSize */ uint32, _ /* criteria */ string, _ Identity) IUserCollection { + return manager.Filter(UserPassThroughFilter) +} + +func (manager *userManager) GetUser(id int64, _ Identity) (IUser, error) { + if user := manager.Find(id); user == nil { + return nil, ERROR_USER_NOT_FOUND + } else { + return user, nil + } +} + +func (manager *userManager) AddUser(identityId int64, github string, editor Identity) (IUser, error) { + userEntity := NewUserEntity(identityId, github) + return manager.Apply(userEntity, repository.Users.Add, manager.cache.Put, editor) +} + +func (manager *userManager) AddUserObject(identityId int64, user IUser, editor Identity) (IUser, error) { + userEntity := NewUserEntity(identityId, user.Github()) + return manager.Apply(userEntity, repository.Users.Add, manager.cache.Put, editor) +} + +func (manager *userManager) AddUserAtomic(transaction ITransaction, identityId int64, github string, editor Identity) (IUser, error) { + userEntity := NewUserEntity(identityId, github) + return manager.ApplyAtomic(transaction, userEntity, repository.Users.AddAtomic, manager.cache.Put, editor) +} + +func (manager *userManager) AddUserObjectAtomic(transaction ITransaction, identityId int64, user IUser, editor Identity) (IUser, error) { + userEntity := NewUserEntity(identityId, user.Github()) + return manager.ApplyAtomic(transaction, userEntity, repository.Users.AddAtomic, manager.cache.Put, editor) +} + +func (manager *userManager) Log(identityId int64, github string, source string, editor Identity, payload string) { + userPipeEntity := NewUserPipeEntity(identityId, github, source, editor.Id(), payload) + repository.Pipe.Insert(userPipeEntity) + + user, err := NewUserFromEntity(userPipeEntity) + if err != nil { + manager.Logger().Error(err) + } else { + manager.cache.Put(user.Id(), user) + } +} + +func (manager *userManager) UpdateUser(id int64, github string, editor Identity) (IUser, error) { + userEntity := NewUserEntity(id, github) + return manager.Apply(userEntity, repository.Users.Update, manager.cache.Put, editor) +} + +func (manager *userManager) UpdateUserObject(id int64, user IUser, editor Identity) (IUser, error) { + userEntity := NewUserEntity(id, user.Github()) + return manager.Apply(userEntity, repository.Users.Update, manager.cache.Put, editor) +} + +func (manager *userManager) UpdateUserAtomic(transaction ITransaction, id int64, github string, editor Identity) (IUser, error) { + userEntity := NewUserEntity(id, github) + return manager.ApplyAtomic(transaction, userEntity, repository.Users.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *userManager) UpdateUserObjectAtomic(transaction ITransaction, id int64, user IUser, editor Identity) (IUser, error) { + userEntity := NewUserEntity(id, user.Github()) + return manager.ApplyAtomic(transaction, userEntity, repository.Users.UpdateAtomic, manager.cache.Put, editor) +} + +func (manager *userManager) AddOrUpdateUserObject(id int64, user IUser, editor Identity) (IUser, error) { + if manager.Exists(id) { + return manager.UpdateUserObject(id, user, editor) + } else { + return manager.AddUserObject(id, user, editor) + } +} + +func (manager *userManager) AddOrUpdateUserObjectAtomic(transaction ITransaction, id int64, user IUser, editor Identity) (IUser, error) { + if manager.Exists(id) { + return manager.UpdateUserObjectAtomic(transaction, id, user, editor) + } else { + return manager.AddUserObjectAtomic(transaction, id, user, editor) + } +} + +func (manager *userManager) RemoveUser(id int64, editor Identity) (IUser, error) { + userEntity := NewUserEntity(id, "") + return manager.Apply(userEntity, repository.Users.Remove, manager.cache.Remove, editor) +} + +func (manager *userManager) RemoveUserAtomic(transaction ITransaction, id int64, editor Identity) (IUser, error) { + userEntity := NewUserEntity(id, "") + return manager.ApplyAtomic(transaction, userEntity, repository.Users.RemoveAtomic, manager.cache.Remove, editor) +} + +func (manager *userManager) Apply(userEntity IUserEntity, repositoryHandler func(IUserEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (IUser, error) { + result, err := NewUserFromEntity(userEntity) + if err != nil { + return nil, err + } + + if err := repositoryHandler(userEntity, editor.Id()); err != nil { + return nil, err + } + + cacheHandler(result.Id(), result) + return result, nil +} + +func (manager *userManager) ApplyAtomic(transaction ITransaction, userEntity IUserEntity, repositoryHandler func(IRepositoryTransaction, IUserEntity, int64) error, cacheHandler func(int64, ISystemObject), editor Identity) (IUser, error) { + result, err := NewUserFromEntity(userEntity) + if err != nil { + return nil, err + } + + transaction.OnCommit(func() { + cacheHandler(result.Id(), result) + }) + + if err := repositoryHandler(transaction, userEntity, editor.Id()); err != nil { + return nil, err + } + + return result, nil +} + +func (manager *userManager) Find(id int64) IUser { + if object, exists := manager.cache.Get(id); !exists { + return nil + } else { + return object.(IUser) + } +} + +func (manager *userManager) ForEach(iterator UserIterator) { + manager.cache.ForEachValue(func(object ISystemObject) { + iterator(object.(IUser)) + }) +} + +func (manager *userManager) Filter(predicate UserFilterPredicate) IUserCollection { + users := NewUsers() + if predicate == nil { + return users + } + + manager.ForEach(func(user IUser) { + if predicate(user) { + users.Append(user) + } + }) + + return users +} + +func (manager *userManager) Map(predicate UserMapPredicate) IUserCollection { + users := NewUsers() + if predicate == nil { + return users + } + + manager.ForEach(func(user IUser) { + users.Append(predicate(user)) + }) + + return users +} diff --git a/greataped/components/core/user_manager_test.go b/greataped/components/core/user_manager_test.go new file mode 100644 index 0000000..45679b1 --- /dev/null +++ b/greataped/components/core/user_manager_test.go @@ -0,0 +1,150 @@ +package core_test + +import ( + "testing" + + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + . "rail.town/infrastructure/components/core" +) + +func TestUserManager_GetName(test *testing.T) { + manager := Conductor.UserManager() + + if manager.Name() != USER_MANAGER { + test.Fail() + } +} + +func TestUserManager_ResolveDependencies(test *testing.T) { + manager := Conductor.UserManager() + + if err := manager.ResolveDependencies(); err != nil { + test.Fatal(err) + } +} + +func TestUserManager_Load(test *testing.T) { + manager := Conductor.UserManager() + + if err := manager.Load(); err != nil { + test.Fatal(err) + } +} + +func TestUserManager_Reload(test *testing.T) { + manager := Conductor.UserManager() + + if err := manager.Reload(); err != nil && err != ERROR_OPERATION_NOT_SUPPORTED { + test.Fatal(err) + } +} + +func TestUserManager_Count(test *testing.T) { + manager := Conductor.UserManager() + + _ = manager.Count() +} + +func TestUserManager_Exists(test *testing.T) { + manager := Conductor.UserManager() + + if manager.Exists(0) { + test.FailNow() + } +} + +func TestUserManager_ListUsers(test *testing.T) { + manager := Conductor.UserManager() + + _ = manager.ListUsers(0, 0, "", nil) +} + +func TestUserManager_GetUser(test *testing.T) { + manager := Conductor.UserManager() + + if user, err := manager.GetUser(0, nil); err == nil { + _ = user + test.FailNow() + } +} + +func TestUserManager_AddUser(test *testing.T) { + manager := Conductor.UserManager() + + user, err := manager.AddUser(0, "github", nil) + if err != nil { + test.Fatal(err) + } + + _ = user +} + +func TestUserManager_UpdateUser(test *testing.T) { + manager := Conductor.UserManager() + + user, err := manager.UpdateUser(0, "github", nil) + if err != nil { + test.Fatal(err) + } + + _ = user +} + +func TestUserManager_RemoveUser(test *testing.T) { + manager := Conductor.UserManager() + + user, err := manager.RemoveUser(0, nil) + if err != nil { + test.Fatal(err) + } + + _ = user +} + +func TestUserManager_Find(test *testing.T) { + manager := Conductor.UserManager() + + user := manager.Find(0) + if user == nil { + test.Fail() + } + + _ = user +} + +func TestUserManager_ForEach(test *testing.T) { + manager := Conductor.UserManager() + + manager.ForEach(func(user IUser) { + _ = user + }) +} + +func TestUserManager_Filter(test *testing.T) { + manager := Conductor.UserManager() + + users := manager.Filter(func(user IUser) bool { + return user.Id() < 0 + }) + + if users.IsNotEmpty() { + test.Fail() + } + + _ = users +} + +func TestUserManager_Map(test *testing.T) { + manager := Conductor.UserManager() + + users := manager.Map(func(user IUser) IUser { + return user + }) + + if users.Count() != manager.Count() { + test.Fail() + } + + _ = users +} diff --git a/greataped/components/main.go b/greataped/components/main.go new file mode 100644 index 0000000..65ec6f4 --- /dev/null +++ b/greataped/components/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "flag" + "runtime" + + "github.com/xeronith/diamante/analytics" + "github.com/xeronith/diamante/logging" + "github.com/xeronith/diamante/server" + "github.com/xeronith/diamante/settings" + "rail.town/infrastructure/components/api/handlers" + "rail.town/infrastructure/components/api/operations" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts" + "rail.town/infrastructure/components/core" + "rail.town/infrastructure/components/model/repository" + "rail.town/infrastructure/providers/outbound/email" + "rail.town/infrastructure/providers/outbound/sms" +) + +var configFilePath = flag.String("config", "config.yaml", "Configuration File Path") + +func main() { + flag.Parse() + if !core.Dockerized { + runtime.GOMAXPROCS(10) + } + + logger := logging.NewLogger(core.Dockerized) + + configuration, err := settings.NewConfiguration(*configFilePath, core.Dockerized) + if err != nil { + logger.Fatal(err) + } + + operationsFactory := operations.NewFactory() + handlersFactory := handlers.NewFactory() + measurementsProvider := analytics.NewInfluxDbProvider(configuration, logger) + emailProvider := email.NewProvider(logger) + smsProvider := sms.NewProvider(logger) + + if mainServer, err := server.New(configuration, operationsFactory, handlersFactory, OPCODES); err != nil { + logger.Fatal(err) + } else { + if err := repository.Initialize(configuration, logger); err != nil { + logger.Fatal(err) + } + + if err := core.Initialize(configuration, logger); err != nil { + logger.Fatal(err) + } + + mainServer.Localizer().Register(Errors) + mainServer.SetSecurityHandler(core.Conductor.IdentityManager()) + mainServer.SetMeasurementsProvider(measurementsProvider) + mainServer.SetEmailProvider(emailProvider) + mainServer.SetSMSProvider(smsProvider) + + mainServer.Start() + } +} diff --git a/greataped/components/model/entity/access_control_entity.go b/greataped/components/model/entity/access_control_entity.go new file mode 100644 index 0000000..e1f5e44 --- /dev/null +++ b/greataped/components/model/entity/access_control_entity.go @@ -0,0 +1,74 @@ +package entity + +import ( + "fmt" + "reflect" + "time" + + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" +) + +var AccessControlEntityType = reflect.TypeOf(accessControlEntity{}) + +// noinspection GoUnusedExportedFunction +func InitializeAccessControlEntity() { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize +} + +type accessControlEntity struct { + entity + KeyField uint64 `json:"key" previous:"id" storage:"BIGINT UNSIGNED" default:"0"` + ValueField uint64 `json:"value" previous:"key" storage:"BIGINT UNSIGNED" default:"0"` +} + +func NewAccessControlEntity(id int64, key uint64, value uint64) IAccessControlEntity { + return &accessControlEntity{ + entity: entity{IdField: id}, + KeyField: key, + ValueField: value, + } +} + +type accessControlPipeEntity struct { + accessControlEntity + pipeEntity +} + +func NewAccessControlPipeEntity(id int64, key uint64, value uint64, source string, editor int64, payload string) IAccessControlPipeEntity { + return &accessControlPipeEntity{ + accessControlEntity: accessControlEntity{ + entity: entity{IdField: id, PayloadField: payload}, + KeyField: key, + ValueField: value, + }, + pipeEntity: pipeEntity{ + Pipe: PIPE_ACCESS_CONTROL, + Source: source, + Editor: editor, + QueueTimestamp: time.Now(), + }, + } +} + +func (entity *accessControlEntity) Key() uint64 { + return entity.KeyField +} + +func (entity *accessControlEntity) Value() uint64 { + return entity.ValueField +} + +func (entity *accessControlEntity) Validate() error { + if entity.IdField <= 0 { + return ERROR_INVALID_ID + } + + return nil +} + +func (entity *accessControlEntity) String() string { + return fmt.Sprintf("AccessControl (Id: %d, Key: %v, Value: %v)", entity.Id(), entity.Key(), entity.Value()) +} diff --git a/greataped/components/model/entity/category_entity.go b/greataped/components/model/entity/category_entity.go new file mode 100644 index 0000000..0d3327c --- /dev/null +++ b/greataped/components/model/entity/category_entity.go @@ -0,0 +1,88 @@ +package entity + +import ( + "fmt" + "reflect" + "time" + + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" +) + +var CategoryEntityType = reflect.TypeOf(categoryEntity{}) + +// noinspection GoUnusedExportedFunction +func InitializeCategoryEntity() { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize +} + +type categoryEntity struct { + entity + CategoryTypeIdField int64 `json:"category_type_id"` + CategoryIdField int64 `json:"category_id"` + TitleField string `json:"title" previous:"category_id" storage:"VARCHAR(64)" default:"''"` + DescriptionField string `json:"description" previous:"title" storage:"VARCHAR(64)" default:"''"` +} + +func NewCategoryEntity(id int64, categoryTypeId int64, categoryId int64, title string, description string) ICategoryEntity { + return &categoryEntity{ + entity: entity{IdField: id}, + CategoryTypeIdField: categoryTypeId, + CategoryIdField: categoryId, + TitleField: title, + DescriptionField: description, + } +} + +type categoryPipeEntity struct { + categoryEntity + pipeEntity +} + +func NewCategoryPipeEntity(id int64, categoryTypeId int64, categoryId int64, title string, description string, source string, editor int64, payload string) ICategoryPipeEntity { + return &categoryPipeEntity{ + categoryEntity: categoryEntity{ + entity: entity{IdField: id, PayloadField: payload}, + CategoryTypeIdField: categoryTypeId, + CategoryIdField: categoryId, + TitleField: title, + DescriptionField: description, + }, + pipeEntity: pipeEntity{ + Pipe: PIPE_CATEGORY, + Source: source, + Editor: editor, + QueueTimestamp: time.Now(), + }, + } +} + +func (entity *categoryEntity) CategoryTypeId() int64 { + return entity.CategoryTypeIdField +} + +func (entity *categoryEntity) CategoryId() int64 { + return entity.CategoryIdField +} + +func (entity *categoryEntity) Title() string { + return entity.TitleField +} + +func (entity *categoryEntity) Description() string { + return entity.DescriptionField +} + +func (entity *categoryEntity) Validate() error { + if entity.IdField <= 0 { + return ERROR_INVALID_ID + } + + return nil +} + +func (entity *categoryEntity) String() string { + return fmt.Sprintf("Category (Id: %d, CategoryTypeId: %d, CategoryId: %d, Title: %v, Description: %v)", entity.Id(), entity.CategoryTypeId(), entity.CategoryId(), entity.Title(), entity.Description()) +} diff --git a/greataped/components/model/entity/category_type_entity.go b/greataped/components/model/entity/category_type_entity.go new file mode 100644 index 0000000..67465d9 --- /dev/null +++ b/greataped/components/model/entity/category_type_entity.go @@ -0,0 +1,67 @@ +package entity + +import ( + "fmt" + "reflect" + "time" + + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" +) + +var CategoryTypeEntityType = reflect.TypeOf(categoryTypeEntity{}) + +// noinspection GoUnusedExportedFunction +func InitializeCategoryTypeEntity() { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize +} + +type categoryTypeEntity struct { + entity + DescriptionField string `json:"description" previous:"id" storage:"VARCHAR(64)" default:"''"` +} + +func NewCategoryTypeEntity(id int64, description string) ICategoryTypeEntity { + return &categoryTypeEntity{ + entity: entity{IdField: id}, + DescriptionField: description, + } +} + +type categoryTypePipeEntity struct { + categoryTypeEntity + pipeEntity +} + +func NewCategoryTypePipeEntity(id int64, description string, source string, editor int64, payload string) ICategoryTypePipeEntity { + return &categoryTypePipeEntity{ + categoryTypeEntity: categoryTypeEntity{ + entity: entity{IdField: id, PayloadField: payload}, + DescriptionField: description, + }, + pipeEntity: pipeEntity{ + Pipe: PIPE_CATEGORY_TYPE, + Source: source, + Editor: editor, + QueueTimestamp: time.Now(), + }, + } +} + +func (entity *categoryTypeEntity) Description() string { + return entity.DescriptionField +} + +func (entity *categoryTypeEntity) Validate() error { + if entity.IdField <= 0 { + return ERROR_INVALID_ID + } + + return nil +} + +func (entity *categoryTypeEntity) String() string { + return fmt.Sprintf("CategoryType (Id: %d, Description: %v)", entity.Id(), entity.Description()) +} diff --git a/greataped/components/model/entity/document_entity.go b/greataped/components/model/entity/document_entity.go new file mode 100644 index 0000000..ac2a39b --- /dev/null +++ b/greataped/components/model/entity/document_entity.go @@ -0,0 +1,67 @@ +package entity + +import ( + "fmt" + "reflect" + "time" + + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" +) + +var DocumentEntityType = reflect.TypeOf(documentEntity{}) + +// noinspection GoUnusedExportedFunction +func InitializeDocumentEntity() { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize +} + +type documentEntity struct { + entity + ContentField string `json:"content" previous:"id" storage:"JSON" default:"'{}'"` +} + +func NewDocumentEntity(id int64, content string) IDocumentEntity { + return &documentEntity{ + entity: entity{IdField: id}, + ContentField: content, + } +} + +type documentPipeEntity struct { + documentEntity + pipeEntity +} + +func NewDocumentPipeEntity(id int64, content string, source string, editor int64, payload string) IDocumentPipeEntity { + return &documentPipeEntity{ + documentEntity: documentEntity{ + entity: entity{IdField: id, PayloadField: payload}, + ContentField: content, + }, + pipeEntity: pipeEntity{ + Pipe: PIPE_DOCUMENT, + Source: source, + Editor: editor, + QueueTimestamp: time.Now(), + }, + } +} + +func (entity *documentEntity) Content() string { + return entity.ContentField +} + +func (entity *documentEntity) Validate() error { + if entity.IdField <= 0 { + return ERROR_INVALID_ID + } + + return nil +} + +func (entity *documentEntity) String() string { + return fmt.Sprintf("Document (Id: %d, Content: %v)", entity.Id(), entity.Content()) +} diff --git a/greataped/components/model/entity/entity.go b/greataped/components/model/entity/entity.go new file mode 100644 index 0000000..6ed24bf --- /dev/null +++ b/greataped/components/model/entity/entity.go @@ -0,0 +1,22 @@ +package entity + +type entity struct { + IdField int64 `json:"id"` + PayloadField string `json:"payload"` +} + +func (entity *entity) Id() int64 { + return entity.IdField +} + +func (entity *entity) Payload() string { + if entity.PayloadField == "" { + entity.PayloadField = "{}" + } + + return entity.PayloadField +} + +func (entity *entity) SetPayload(payload string) { + entity.PayloadField = payload +} diff --git a/greataped/components/model/entity/identity_entity.go b/greataped/components/model/entity/identity_entity.go new file mode 100644 index 0000000..d944657 --- /dev/null +++ b/greataped/components/model/entity/identity_entity.go @@ -0,0 +1,207 @@ +package entity + +import ( + "fmt" + "reflect" + "time" + + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" +) + +var IdentityEntityType = reflect.TypeOf(identityEntity{}) + +// noinspection GoUnusedExportedFunction +func InitializeIdentityEntity() { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize +} + +type identityEntity struct { + entity + UsernameField string `json:"username" previous:"id" storage:"VARCHAR(32)" default:"''"` + PhoneNumberField string `json:"phone_number" previous:"username" storage:"VARCHAR(12)" default:"''"` + PhoneNumberConfirmedField bool `json:"phone_number_confirmed" previous:"phone_number" storage:"BIT(1)" default:"FALSE"` + FirstNameField string `json:"first_name" previous:"phone_number_confirmed" storage:"VARCHAR(128)" default:"''"` + LastNameField string `json:"last_name" previous:"first_name" storage:"VARCHAR(128)" default:"''"` + DisplayNameField string `json:"display_name" previous:"last_name" storage:"VARCHAR(128)" default:"''"` + EmailField string `json:"email" previous:"display_name" storage:"VARCHAR(128)" default:"''"` + EmailConfirmedField bool `json:"email_confirmed" previous:"email" storage:"BIT(1)" default:"FALSE"` + AvatarField string `json:"avatar" previous:"email_confirmed" storage:"VARCHAR(512)" default:"''"` + BannerField string `json:"banner" previous:"avatar" storage:"VARCHAR(512)" default:"''"` + SummaryField string `json:"summary" previous:"banner" storage:"VARCHAR(512)" default:"''"` + TokenField string `json:"token" previous:"summary" storage:"VARCHAR(256)" default:"''"` + MultiFactorField bool `json:"multi_factor" previous:"token" storage:"BIT(1)" default:"FALSE"` + HashField string `json:"hash" previous:"multi_factor" storage:"VARCHAR(256)" default:"''"` + SaltField string `json:"salt" previous:"hash" storage:"VARCHAR(64)" default:"''"` + PublicKeyField string `json:"public_key" previous:"salt" storage:"VARCHAR(4096)" default:"''"` + PrivateKeyField string `json:"private_key" previous:"public_key" storage:"VARCHAR(4096)" default:"''"` + PermissionField uint64 `json:"permission" previous:"private_key" storage:"BIGINT UNSIGNED" default:"0"` + RestrictionField uint32 `json:"restriction" previous:"permission" storage:"INT UNSIGNED" default:"0"` + LastLoginField int64 `json:"last_login" previous:"restriction" storage:"BIGINT" default:"0"` + LoginCountField uint32 `json:"login_count" previous:"last_login" storage:"INT UNSIGNED" default:"0"` +} + +func NewIdentityEntity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32) IIdentityEntity { + return &identityEntity{ + entity: entity{IdField: id}, + UsernameField: username, + PhoneNumberField: phoneNumber, + PhoneNumberConfirmedField: phoneNumberConfirmed, + FirstNameField: firstName, + LastNameField: lastName, + DisplayNameField: displayName, + EmailField: email, + EmailConfirmedField: emailConfirmed, + AvatarField: avatar, + BannerField: banner, + SummaryField: summary, + TokenField: token, + MultiFactorField: multiFactor, + HashField: hash, + SaltField: salt, + PublicKeyField: publicKey, + PrivateKeyField: privateKey, + PermissionField: permission, + RestrictionField: restriction, + LastLoginField: lastLogin, + LoginCountField: loginCount, + } +} + +type identityPipeEntity struct { + identityEntity + pipeEntity +} + +func NewIdentityPipeEntity(id int64, username string, phoneNumber string, phoneNumberConfirmed bool, firstName string, lastName string, displayName string, email string, emailConfirmed bool, avatar string, banner string, summary string, token string, multiFactor bool, hash string, salt string, publicKey string, privateKey string, permission uint64, restriction uint32, lastLogin int64, loginCount uint32, source string, editor int64, payload string) IIdentityPipeEntity { + return &identityPipeEntity{ + identityEntity: identityEntity{ + entity: entity{IdField: id, PayloadField: payload}, + UsernameField: username, + PhoneNumberField: phoneNumber, + PhoneNumberConfirmedField: phoneNumberConfirmed, + FirstNameField: firstName, + LastNameField: lastName, + DisplayNameField: displayName, + EmailField: email, + EmailConfirmedField: emailConfirmed, + AvatarField: avatar, + BannerField: banner, + SummaryField: summary, + TokenField: token, + MultiFactorField: multiFactor, + HashField: hash, + SaltField: salt, + PublicKeyField: publicKey, + PrivateKeyField: privateKey, + PermissionField: permission, + RestrictionField: restriction, + LastLoginField: lastLogin, + LoginCountField: loginCount, + }, + pipeEntity: pipeEntity{ + Pipe: PIPE_IDENTITY, + Source: source, + Editor: editor, + QueueTimestamp: time.Now(), + }, + } +} + +func (entity *identityEntity) Username() string { + return entity.UsernameField +} + +func (entity *identityEntity) PhoneNumber() string { + return entity.PhoneNumberField +} + +func (entity *identityEntity) PhoneNumberConfirmed() bool { + return entity.PhoneNumberConfirmedField +} + +func (entity *identityEntity) FirstName() string { + return entity.FirstNameField +} + +func (entity *identityEntity) LastName() string { + return entity.LastNameField +} + +func (entity *identityEntity) DisplayName() string { + return entity.DisplayNameField +} + +func (entity *identityEntity) Email() string { + return entity.EmailField +} + +func (entity *identityEntity) EmailConfirmed() bool { + return entity.EmailConfirmedField +} + +func (entity *identityEntity) Avatar() string { + return entity.AvatarField +} + +func (entity *identityEntity) Banner() string { + return entity.BannerField +} + +func (entity *identityEntity) Summary() string { + return entity.SummaryField +} + +func (entity *identityEntity) Token() string { + return entity.TokenField +} + +func (entity *identityEntity) MultiFactor() bool { + return entity.MultiFactorField +} + +func (entity *identityEntity) Hash() string { + return entity.HashField +} + +func (entity *identityEntity) Salt() string { + return entity.SaltField +} + +func (entity *identityEntity) PublicKey() string { + return entity.PublicKeyField +} + +func (entity *identityEntity) PrivateKey() string { + return entity.PrivateKeyField +} + +func (entity *identityEntity) Permission() uint64 { + return entity.PermissionField +} + +func (entity *identityEntity) Restriction() uint32 { + return entity.RestrictionField +} + +func (entity *identityEntity) LastLogin() int64 { + return entity.LastLoginField +} + +func (entity *identityEntity) LoginCount() uint32 { + return entity.LoginCountField +} + +func (entity *identityEntity) Validate() error { + if entity.IdField <= 0 { + return ERROR_INVALID_ID + } + + return nil +} + +func (entity *identityEntity) String() string { + return fmt.Sprintf("Identity (Id: %d, Username: %v, PhoneNumber: %v, PhoneNumberConfirmed: %v, FirstName: %v, LastName: %v, DisplayName: %v, Email: %v, EmailConfirmed: %v, Avatar: %v, Banner: %v, Summary: %v, Token: %v, MultiFactor: %v, Hash: %v, Salt: %v, PublicKey: %v, PrivateKey: %v, Permission: %v, Restriction: %v, LastLogin: %v, LoginCount: %v)", entity.Id(), entity.Username(), entity.PhoneNumber(), entity.PhoneNumberConfirmed(), entity.FirstName(), entity.LastName(), entity.DisplayName(), entity.Email(), entity.EmailConfirmed(), entity.Avatar(), entity.Banner(), entity.Summary(), entity.Token(), entity.MultiFactor(), entity.Hash(), entity.Salt(), entity.PublicKey(), entity.PrivateKey(), entity.Permission(), entity.Restriction(), entity.LastLogin(), entity.LoginCount()) +} diff --git a/greataped/components/model/entity/pipe.go b/greataped/components/model/entity/pipe.go new file mode 100644 index 0000000..a549536 --- /dev/null +++ b/greataped/components/model/entity/pipe.go @@ -0,0 +1,26 @@ +package entity + +import . "time" + +type pipeEntity struct { + Pipe int `json:"pipe"` + Source string `json:"source"` + Editor int64 `json:"editor"` + QueueTimestamp Time `json:"queued_at"` +} + +func (entity *pipeEntity) GetPipe() int { + return entity.Pipe +} + +func (entity *pipeEntity) GetSource() string { + return entity.Source +} + +func (entity *pipeEntity) GetEditor() int64 { + return entity.Editor +} + +func (entity *pipeEntity) GetQueueTimestamp() Time { + return entity.QueueTimestamp +} diff --git a/greataped/components/model/entity/remote_activity_entity.go b/greataped/components/model/entity/remote_activity_entity.go new file mode 100644 index 0000000..4565b40 --- /dev/null +++ b/greataped/components/model/entity/remote_activity_entity.go @@ -0,0 +1,116 @@ +package entity + +import ( + "fmt" + "reflect" + "time" + + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" +) + +var RemoteActivityEntityType = reflect.TypeOf(remoteActivityEntity{}) + +// noinspection GoUnusedExportedFunction +func InitializeRemoteActivityEntity() { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize +} + +type remoteActivityEntity struct { + entity + EntryPointField string `json:"entry_point" previous:"id" storage:"VARCHAR(256)" default:"''"` + DurationField int64 `json:"duration" previous:"entry_point" storage:"BIGINT" default:"0"` + SuccessfulField bool `json:"successful" previous:"duration" storage:"BIT(1)" default:"FALSE"` + ErrorMessageField string `json:"error_message" previous:"successful" storage:"VARCHAR(1024)" default:"''"` + RemoteAddressField string `json:"remote_address" previous:"error_message" storage:"VARCHAR(128)" default:"''"` + UserAgentField string `json:"user_agent" previous:"remote_address" storage:"VARCHAR(512)" default:"''"` + EventTypeField uint32 `json:"event_type" previous:"user_agent" storage:"INT UNSIGNED" default:"0"` + TimestampField int64 `json:"timestamp" previous:"event_type" storage:"BIGINT" default:"0"` +} + +func NewRemoteActivityEntity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64) IRemoteActivityEntity { + return &remoteActivityEntity{ + entity: entity{IdField: id}, + EntryPointField: entryPoint, + DurationField: duration, + SuccessfulField: successful, + ErrorMessageField: errorMessage, + RemoteAddressField: remoteAddress, + UserAgentField: userAgent, + EventTypeField: eventType, + TimestampField: timestamp, + } +} + +type remoteActivityPipeEntity struct { + remoteActivityEntity + pipeEntity +} + +func NewRemoteActivityPipeEntity(id int64, entryPoint string, duration int64, successful bool, errorMessage string, remoteAddress string, userAgent string, eventType uint32, timestamp int64, source string, editor int64, payload string) IRemoteActivityPipeEntity { + return &remoteActivityPipeEntity{ + remoteActivityEntity: remoteActivityEntity{ + entity: entity{IdField: id, PayloadField: payload}, + EntryPointField: entryPoint, + DurationField: duration, + SuccessfulField: successful, + ErrorMessageField: errorMessage, + RemoteAddressField: remoteAddress, + UserAgentField: userAgent, + EventTypeField: eventType, + TimestampField: timestamp, + }, + pipeEntity: pipeEntity{ + Pipe: PIPE_REMOTE_ACTIVITY, + Source: source, + Editor: editor, + QueueTimestamp: time.Now(), + }, + } +} + +func (entity *remoteActivityEntity) EntryPoint() string { + return entity.EntryPointField +} + +func (entity *remoteActivityEntity) Duration() int64 { + return entity.DurationField +} + +func (entity *remoteActivityEntity) Successful() bool { + return entity.SuccessfulField +} + +func (entity *remoteActivityEntity) ErrorMessage() string { + return entity.ErrorMessageField +} + +func (entity *remoteActivityEntity) RemoteAddress() string { + return entity.RemoteAddressField +} + +func (entity *remoteActivityEntity) UserAgent() string { + return entity.UserAgentField +} + +func (entity *remoteActivityEntity) EventType() uint32 { + return entity.EventTypeField +} + +func (entity *remoteActivityEntity) Timestamp() int64 { + return entity.TimestampField +} + +func (entity *remoteActivityEntity) Validate() error { + if entity.IdField <= 0 { + return ERROR_INVALID_ID + } + + return nil +} + +func (entity *remoteActivityEntity) String() string { + return fmt.Sprintf("RemoteActivity (Id: %d, EntryPoint: %v, Duration: %v, Successful: %v, ErrorMessage: %v, RemoteAddress: %v, UserAgent: %v, EventType: %v, Timestamp: %v)", entity.Id(), entity.EntryPoint(), entity.Duration(), entity.Successful(), entity.ErrorMessage(), entity.RemoteAddress(), entity.UserAgent(), entity.EventType(), entity.Timestamp()) +} diff --git a/greataped/components/model/entity/system_schedule_entity.go b/greataped/components/model/entity/system_schedule_entity.go new file mode 100644 index 0000000..dad5dfa --- /dev/null +++ b/greataped/components/model/entity/system_schedule_entity.go @@ -0,0 +1,74 @@ +package entity + +import ( + "fmt" + "reflect" + "time" + + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" +) + +var SystemScheduleEntityType = reflect.TypeOf(systemScheduleEntity{}) + +// noinspection GoUnusedExportedFunction +func InitializeSystemScheduleEntity() { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize +} + +type systemScheduleEntity struct { + entity + EnabledField bool `json:"enabled" previous:"id" storage:"BIT(1)" default:"FALSE"` + ConfigField string `json:"config" previous:"enabled" storage:"VARCHAR(1024)" default:"''"` +} + +func NewSystemScheduleEntity(id int64, enabled bool, config string) ISystemScheduleEntity { + return &systemScheduleEntity{ + entity: entity{IdField: id}, + EnabledField: enabled, + ConfigField: config, + } +} + +type systemSchedulePipeEntity struct { + systemScheduleEntity + pipeEntity +} + +func NewSystemSchedulePipeEntity(id int64, enabled bool, config string, source string, editor int64, payload string) ISystemSchedulePipeEntity { + return &systemSchedulePipeEntity{ + systemScheduleEntity: systemScheduleEntity{ + entity: entity{IdField: id, PayloadField: payload}, + EnabledField: enabled, + ConfigField: config, + }, + pipeEntity: pipeEntity{ + Pipe: PIPE_SYSTEM_SCHEDULE, + Source: source, + Editor: editor, + QueueTimestamp: time.Now(), + }, + } +} + +func (entity *systemScheduleEntity) Enabled() bool { + return entity.EnabledField +} + +func (entity *systemScheduleEntity) Config() string { + return entity.ConfigField +} + +func (entity *systemScheduleEntity) Validate() error { + if entity.IdField <= 0 { + return ERROR_INVALID_ID + } + + return nil +} + +func (entity *systemScheduleEntity) String() string { + return fmt.Sprintf("SystemSchedule (Id: %d, Enabled: %v, Config: %v)", entity.Id(), entity.Enabled(), entity.Config()) +} diff --git a/greataped/components/model/entity/user_entity.go b/greataped/components/model/entity/user_entity.go new file mode 100644 index 0000000..5ecaafd --- /dev/null +++ b/greataped/components/model/entity/user_entity.go @@ -0,0 +1,67 @@ +package entity + +import ( + "fmt" + "reflect" + "time" + + "rail.town/infrastructure/app/validators" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" +) + +var UserEntityType = reflect.TypeOf(userEntity{}) + +// noinspection GoUnusedExportedFunction +func InitializeUserEntity() { + _ = ENABLE_CUSTOM_ERRORS + _ = validators.Initialize +} + +type userEntity struct { + entity + GithubField string `json:"github" previous:"id" storage:"VARCHAR(512)" default:"''"` +} + +func NewUserEntity(id int64, github string) IUserEntity { + return &userEntity{ + entity: entity{IdField: id}, + GithubField: github, + } +} + +type userPipeEntity struct { + userEntity + pipeEntity +} + +func NewUserPipeEntity(id int64, github string, source string, editor int64, payload string) IUserPipeEntity { + return &userPipeEntity{ + userEntity: userEntity{ + entity: entity{IdField: id, PayloadField: payload}, + GithubField: github, + }, + pipeEntity: pipeEntity{ + Pipe: PIPE_USER, + Source: source, + Editor: editor, + QueueTimestamp: time.Now(), + }, + } +} + +func (entity *userEntity) Github() string { + return entity.GithubField +} + +func (entity *userEntity) Validate() error { + if entity.IdField <= 0 { + return ERROR_INVALID_ID + } + + return nil +} + +func (entity *userEntity) String() string { + return fmt.Sprintf("User (Id: %d, Github: %v)", entity.Id(), entity.Github()) +} diff --git a/greataped/components/model/repository/access_controls_repository.go b/greataped/components/model/repository/access_controls_repository.go new file mode 100644 index 0000000..7149528 --- /dev/null +++ b/greataped/components/model/repository/access_controls_repository.go @@ -0,0 +1,173 @@ +package repository + +import ( + . "github.com/xeronith/diamante/contracts/database" + . "github.com/xeronith/diamante/contracts/logging" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" +) + +type accessControlsRepository struct { + baseRepository +} + +func newAccessControlsRepository(logger ILogger) IAccessControlsRepository { + return &accessControlsRepository{ + baseRepository: newBaseRepository("access_control", "access_controls", AccessControlEntityType, logger, false), + } +} + +func (repository *accessControlsRepository) Add(entity IAccessControlEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `access_controls` (`id`, `key`, `value`, `editor`) VALUES (?, ?, ?, ?);" + return repository.database.InsertSingle(query, entity.Id(), entity.Key(), entity.Value(), editor) +} + +func (repository *accessControlsRepository) AddAtomic(transaction IRepositoryTransaction, entity IAccessControlEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `access_controls` (`id`, `key`, `value`, `editor`) VALUES (?, ?, ?, ?);" + return repository.database.InsertSingleAtomic(transaction, query, entity.Id(), entity.Key(), entity.Value(), editor) +} + +func (repository *accessControlsRepository) FetchById(id int64) (IAccessControlEntity, error) { + if id <= 0 { + return nil, ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "SELECT `id`, `key`, `value` FROM `access_controls` WHERE `id` = ? AND `status` = 0;" + + var accessControlEntity IAccessControlEntity + if err := repository.database.QuerySingle(func(cursor ICursor) error { + var ( + id int64 + key uint64 + value uint64 + ) + + if err := cursor.Scan(&id, &key, &value); err != nil { + return err + } + + accessControlEntity = NewAccessControlEntity(id, key, value) + return nil + }, query, id); err != nil { + return nil, err + } + + return accessControlEntity, nil +} + +func (repository *accessControlsRepository) Update(entity IAccessControlEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `access_controls` SET `key` = ?, `value` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, entity.Key(), entity.Value(), editor, entity.Id()) +} + +func (repository *accessControlsRepository) UpdateAtomic(transaction IRepositoryTransaction, entity IAccessControlEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `access_controls` SET `key` = ?, `value` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, entity.Key(), entity.Value(), editor, entity.Id()) +} + +func (repository *accessControlsRepository) Remove(entity IAccessControlEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `access_controls` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingle(query, editor, entity.Id()) +} + +func (repository *accessControlsRepository) RemoveAtomic(transaction IRepositoryTransaction, entity IAccessControlEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `access_controls` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingleAtomic(transaction, query, editor, entity.Id()) +} + +func (repository *accessControlsRepository) FetchAll() (AccessControlEntities, error) { + // language=SQL + query := "SELECT `id`, `key`, `value` FROM `access_controls` WHERE `id` > 0 AND `status` = 0;" + + var accessControlEntities AccessControlEntities + if err := repository.database.Query(func(cursor ICursor) error { + var ( + id int64 + key uint64 + value uint64 + ) + + if err := cursor.Scan(&id, &key, &value); err != nil { + return err + } + + accessControlEntities = append(accessControlEntities, NewAccessControlEntity(id, key, value)) + return nil + }, query); err != nil { + return nil, err + } + + return accessControlEntities, nil +} + +func (repository *accessControlsRepository) UpdateKey(id int64, value uint64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `access_controls` SET `key` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *accessControlsRepository) UpdateKeyAtomic(transaction IRepositoryTransaction, id int64, value uint64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `access_controls` SET `key` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *accessControlsRepository) UpdateValue(id int64, value uint64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `access_controls` SET `value` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *accessControlsRepository) UpdateValueAtomic(transaction IRepositoryTransaction, id int64, value uint64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `access_controls` SET `value` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} diff --git a/greataped/components/model/repository/access_controls_repository_test.go b/greataped/components/model/repository/access_controls_repository_test.go new file mode 100644 index 0000000..72dc5c5 --- /dev/null +++ b/greataped/components/model/repository/access_controls_repository_test.go @@ -0,0 +1,301 @@ +package repository_test + +import ( + "testing" + + . "rail.town/infrastructure/components/model/entity" + . "rail.town/infrastructure/components/model/repository" +) + +func TestAccessControlsRepository_Add(test *testing.T) { + type arguments struct { + id int64 + key uint64 + value uint64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + key: 0, + value: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + key: 0, + value: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + key: 0, + value: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewAccessControlEntity(testCase.arguments.id, testCase.arguments.key, testCase.arguments.value) + if result := AccessControls.Add(entity, -1) == nil; result != testCase.expectation { + test.Errorf("AccessControls.Add() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestAccessControlsRepository_FetchById(test *testing.T) { + type arguments struct { + id int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity, err := AccessControls.FetchById(testCase.arguments.id) + if result := err == nil; result != testCase.expectation { + test.Errorf("AccessControls.FetchById() = %v, expected %v", result, testCase.expectation) + } + + _ = entity + }) + } +} + +func TestAccessControlsRepository_Update(test *testing.T) { + type arguments struct { + id int64 + key uint64 + value uint64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + key: 0, + value: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + key: 0, + value: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + key: 0, + value: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewAccessControlEntity(testCase.arguments.id, testCase.arguments.key, testCase.arguments.value) + if result := AccessControls.Update(entity, -1) == nil; result != testCase.expectation { + test.Errorf("AccessControls.Update() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestAccessControlsRepository_Remove(test *testing.T) { + type arguments struct { + id int64 + key uint64 + value uint64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewAccessControlEntity(testCase.arguments.id, testCase.arguments.key, testCase.arguments.value) + if result := AccessControls.Remove(entity, -1) == nil; result != testCase.expectation { + test.Errorf("AccessControls.Remove() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestAccessControlsRepository_FetchAll(test *testing.T) { + entities, err := AccessControls.FetchAll() + if err != nil { + test.Fatal(err) + } + + _ = entities +} + +func TestAccessControlsRepository_UpdateKey(test *testing.T) { + type arguments struct { + id int64 + key uint64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + key: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + key: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + key: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := AccessControls.UpdateKey(testCase.arguments.id, testCase.arguments.key, -1) == nil; result != testCase.expectation { + test.Errorf("AccessControls.UpdateKey() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestAccessControlsRepository_UpdateValue(test *testing.T) { + type arguments struct { + id int64 + value uint64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + value: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + value: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + value: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := AccessControls.UpdateValue(testCase.arguments.id, testCase.arguments.value, -1) == nil; result != testCase.expectation { + test.Errorf("AccessControls.UpdateValue() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/components/model/repository/base_repository.go b/greataped/components/model/repository/base_repository.go new file mode 100644 index 0000000..f4ca338 --- /dev/null +++ b/greataped/components/model/repository/base_repository.go @@ -0,0 +1,193 @@ +package repository + +import ( + "embed" + "encoding/json" + "fmt" + "io/ioutil" + "math/rand" + "reflect" + "strings" + "time" + + . "github.com/xeronith/diamante/contracts/database" + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/system" +) + +//go:embed scripts +var scripts embed.FS + +type baseRepository struct { + name string + pluralName string + entityType reflect.Type + logger ILogger + database ISqlDatabase + volatile bool +} + +func newBaseRepository(name, pluralName string, entityType reflect.Type, logger ILogger, volatile bool) baseRepository { + return baseRepository{ + name: name, + pluralName: pluralName, + entityType: entityType, + logger: logger, + volatile: volatile, + } +} + +func (repository *baseRepository) Name() string { + return repository.name +} + +func (repository *baseRepository) Migrate() error { + schema := repository.GetSqlDatabase().GetSchema() + if schema == nil { + return fmt.Errorf("invalid_db_schema: %s", repository.GetSqlDatabase().GetName()) + } + + if !repository.volatile { + createTablesScript := repository.LoadScript("scripts/%s.sql") + createTriggersScript := repository.LoadScript("scripts/%s_triggers.sql") + + if !schema.HasTable(repository.pluralName) { + script := createTablesScript + createTriggersScript + if err := repository.GetSqlDatabase().RunScript(script); err != nil { + return err + } + + schema = repository.GetSqlDatabase().GetSchema() + + if !schema.HasTable(repository.pluralName) { + return fmt.Errorf("DB_MIGRATION: %s", repository.pluralName) + } + + if !schema.HasHistoryTable(repository.pluralName) { + return fmt.Errorf("DB_MIGRATION: history.%s", repository.pluralName) + } + + if !schema.HasTrigger(fmt.Sprintf("%s_after_update", repository.pluralName)) { + return fmt.Errorf("DB_MIGRATION: %s_after_update", repository.pluralName) + } + + if !schema.HasTrigger(fmt.Sprintf("%s_after_delete", repository.pluralName)) { + return fmt.Errorf("DB_MIGRATION: %s_after_delete", repository.pluralName) + } + + _, err := repository.database.Execute("INSERT INTO `__system__` (`script`) VALUES (?);", script) + if err != nil { + repository.logger.Alert(fmt.Sprintf("DB_MIGRATION: %s", err)) + } + + repository.logger.Debug(fmt.Sprintf("DB_MIGRATION: ✓ %s.%s", repository.GetSqlDatabase().GetName(), repository.pluralName)) + } + + changes := make([]string, 0) + commands := make([]string, 0) + historyCommands := make([]string, 0) + + for i := 0; i < repository.entityType.NumField(); i++ { + field := repository.entityType.Field(i) + column := field.Tag.Get("json") + dbType := field.Tag.Get("storage") + defaultValue := field.Tag.Get("default") + previousColumn := field.Tag.Get("previous") + + if field.Name != "entity" && !schema.HasColumn(repository.pluralName, column) { + changes = append(changes, fmt.Sprintf("%s.%s", repository.pluralName, column)) + commands = append(commands, fmt.Sprintf("ALTER TABLE `%s` ADD COLUMN `%s` %s NOT NULL DEFAULT %s AFTER `%s`;", repository.pluralName, column, dbType, defaultValue, previousColumn)) + + if previousColumn == "id" { + historyCommands = append(historyCommands, fmt.Sprintf("ALTER TABLE `%s` ADD COLUMN `%s` %s NOT NULL DEFAULT %s AFTER `%s`;", repository.pluralName, column, dbType, defaultValue, "original_id")) + } else { + historyCommands = append(historyCommands, fmt.Sprintf("ALTER TABLE `%s` ADD COLUMN `%s` %s NOT NULL DEFAULT %s AFTER `%s`;", repository.pluralName, column, dbType, defaultValue, previousColumn)) + } + } + } + + scriptLines := make([]string, 0) + if len(commands) > 0 { + scriptLines = append([]string{fmt.Sprintf("USE `%s_history`;\n", repository.GetSqlDatabase().GetName())}, historyCommands...) + scriptLines = append(scriptLines, fmt.Sprintf("\nUSE `%s`;\n", repository.GetSqlDatabase().GetName())) + scriptLines = append(scriptLines, fmt.Sprintf("DROP TRIGGER `%s_after_update`;", repository.pluralName)) + scriptLines = append(scriptLines, fmt.Sprintf("DROP TRIGGER `%s_after_delete`;\n", repository.pluralName)) + scriptLines = append(scriptLines, commands...) + scriptLines = append(scriptLines, createTriggersScript) + script := strings.Join(scriptLines, "\n") + + if err := repository.GetSqlDatabase().RunScript(script); err != nil { + return err + } + + schema = repository.GetSqlDatabase().GetSchema() + + for i := 0; i < repository.entityType.NumField(); i++ { + field := repository.entityType.Field(i) + column := field.Tag.Get("json") + + if field.Name != "entity" && !schema.HasColumn(repository.pluralName, column) { + return fmt.Errorf("DB_MIGRATION: %s.%s", repository.pluralName, column) + } + } + + if !schema.HasTrigger(fmt.Sprintf("%s_after_update", repository.pluralName)) { + return fmt.Errorf("DB_MIGRATION: %s_after_update", repository.pluralName) + } + + if !schema.HasTrigger(fmt.Sprintf("%s_after_delete", repository.pluralName)) { + return fmt.Errorf("DB_MIGRATION: %s_after_delete", repository.pluralName) + } + + _, err := repository.database.Execute("INSERT INTO `__system__` (`script`) VALUES (?);", script) + if err != nil { + repository.logger.Alert(fmt.Sprintf("DB_MIGRATION: %s", err)) + } + + for _, change := range changes { + repository.logger.Debug(fmt.Sprintf("DB_MIGRATION: ✓ %s.%s", repository.GetSqlDatabase().GetName(), change)) + } + } + } + + return nil +} + +func (repository *baseRepository) LoadScript(resource string) string { + scriptData, err := scripts.ReadFile(fmt.Sprintf(resource, repository.name)) + if err != nil { + panic(err) + } + + return strings.ReplaceAll(string(scriptData), "###DATABASE###", repository.GetSqlDatabase().GetName()) +} + +func (repository *baseRepository) GetSqlDatabase() ISqlDatabase { + return repository.database +} + +func (repository *baseRepository) SetSqlDatabase(database ISqlDatabase) { + repository.database = database +} + +func (repository *baseRepository) Serialize(pointer Pointer, cause error) { + if data, err := json.Marshal(pointer); err != nil { + //TODO: Handle + repository.logger.Critical(fmt.Sprintf("EMERGENCY SERIALIZATION MARSHALLING FAILURE")) + } else { + timestamp := time.Now().UnixNano() + randomFactor := rand.Intn(1000) + dataFileName := fmt.Sprintf("%sE_%d_%d.json", repository.logger.SerializationPath(), timestamp, randomFactor) + + if err := ioutil.WriteFile(dataFileName, data, 0644); err != nil { + //TODO: Handle + repository.logger.Critical(fmt.Sprintf("EMERGENCY SERIALIZATION PERSISTENCE FAILURE: %s", err)) + } + + errorFileName := fmt.Sprintf("%sE_%d_%d_ERROR.log", repository.logger.SerializationPath(), timestamp, randomFactor) + if err := ioutil.WriteFile(errorFileName, []byte(cause.Error()), 0644); err != nil { + //TODO: Handle + repository.logger.Critical(fmt.Sprintf("EMERGENCY SERIALIZATION ERROR PERSISTENCE FAILURE: %s", err)) + } + } +} diff --git a/greataped/components/model/repository/base_repository_test.go b/greataped/components/model/repository/base_repository_test.go new file mode 100644 index 0000000..a7b734e --- /dev/null +++ b/greataped/components/model/repository/base_repository_test.go @@ -0,0 +1,24 @@ +package repository_test + +import ( + "os" + "testing" + + "github.com/xeronith/diamante/logging" + "github.com/xeronith/diamante/settings" + "rail.town/infrastructure/components/model/repository" +) + +//region Initialization + +func TestMain(main *testing.M) { + logger := logging.NewLogger(false) + configuration := settings.NewTestConfiguration() + if err := repository.Initialize(configuration, logger); err != nil { + os.Exit(1) + } + + os.Exit(main.Run()) +} + +//endregion diff --git a/greataped/components/model/repository/categories_repository.go b/greataped/components/model/repository/categories_repository.go new file mode 100644 index 0000000..a17266f --- /dev/null +++ b/greataped/components/model/repository/categories_repository.go @@ -0,0 +1,221 @@ +package repository + +import ( + . "github.com/xeronith/diamante/contracts/database" + . "github.com/xeronith/diamante/contracts/logging" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" +) + +type categoriesRepository struct { + baseRepository +} + +func newCategoriesRepository(logger ILogger) ICategoriesRepository { + return &categoriesRepository{ + baseRepository: newBaseRepository("category", "categories", CategoryEntityType, logger, false), + } +} + +func (repository *categoriesRepository) Add(entity ICategoryEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `categories` (`id`, `category_type_id`, `category_id`, `title`, `description`, `editor`) VALUES (?, ?, ?, ?, ?, ?);" + return repository.database.InsertSingle(query, entity.Id(), entity.CategoryTypeId(), entity.CategoryId(), entity.Title(), entity.Description(), editor) +} + +func (repository *categoriesRepository) AddAtomic(transaction IRepositoryTransaction, entity ICategoryEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `categories` (`id`, `category_type_id`, `category_id`, `title`, `description`, `editor`) VALUES (?, ?, ?, ?, ?, ?);" + return repository.database.InsertSingleAtomic(transaction, query, entity.Id(), entity.CategoryTypeId(), entity.CategoryId(), entity.Title(), entity.Description(), editor) +} + +func (repository *categoriesRepository) FetchById(id int64) (ICategoryEntity, error) { + if id <= 0 { + return nil, ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "SELECT `id`, `category_type_id`, `category_id`, `title`, `description` FROM `categories` WHERE `id` = ? AND `status` = 0;" + + var categoryEntity ICategoryEntity + if err := repository.database.QuerySingle(func(cursor ICursor) error { + var ( + id int64 + categoryTypeId int64 + categoryId int64 + title string + description string + ) + + if err := cursor.Scan(&id, &categoryTypeId, &categoryId, &title, &description); err != nil { + return err + } + + categoryEntity = NewCategoryEntity(id, categoryTypeId, categoryId, title, description) + return nil + }, query, id); err != nil { + return nil, err + } + + return categoryEntity, nil +} + +func (repository *categoriesRepository) Update(entity ICategoryEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `categories` SET `category_type_id` = ?, `category_id` = ?, `title` = ?, `description` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, entity.CategoryTypeId(), entity.CategoryId(), entity.Title(), entity.Description(), editor, entity.Id()) +} + +func (repository *categoriesRepository) UpdateAtomic(transaction IRepositoryTransaction, entity ICategoryEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `categories` SET `category_type_id` = ?, `category_id` = ?, `title` = ?, `description` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, entity.CategoryTypeId(), entity.CategoryId(), entity.Title(), entity.Description(), editor, entity.Id()) +} + +func (repository *categoriesRepository) Remove(entity ICategoryEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `categories` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingle(query, editor, entity.Id()) +} + +func (repository *categoriesRepository) RemoveAtomic(transaction IRepositoryTransaction, entity ICategoryEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `categories` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingleAtomic(transaction, query, editor, entity.Id()) +} + +func (repository *categoriesRepository) FetchAll() (CategoryEntities, error) { + // language=SQL + query := "SELECT `id`, `category_type_id`, `category_id`, `title`, `description` FROM `categories` WHERE `id` > 0 AND `status` = 0;" + + var categoryEntities CategoryEntities + if err := repository.database.Query(func(cursor ICursor) error { + var ( + id int64 + categoryTypeId int64 + categoryId int64 + title string + description string + ) + + if err := cursor.Scan(&id, &categoryTypeId, &categoryId, &title, &description); err != nil { + return err + } + + categoryEntities = append(categoryEntities, NewCategoryEntity(id, categoryTypeId, categoryId, title, description)) + return nil + }, query); err != nil { + return nil, err + } + + return categoryEntities, nil +} + +func (repository *categoriesRepository) FetchAllByCategoryType(categoryTypeId int64) (CategoryEntities, error) { + if categoryTypeId <= 0 { + return nil, ERROR_INVALID_PARAMETERS + } + + return repository.FetchAllByDependency("category_type_id", categoryTypeId) +} + +func (repository *categoriesRepository) FetchAllByCategory(categoryId int64) (CategoryEntities, error) { + if categoryId <= 0 { + return nil, ERROR_INVALID_PARAMETERS + } + + return repository.FetchAllByDependency("category_id", categoryId) +} + +func (repository *categoriesRepository) FetchAllByDependency(dependencyName string, dependencyId int64) (CategoryEntities, error) { + // language=SQL + query := "SELECT `id`, `category_type_id`, `category_id`, `title`, `description` FROM `categories` WHERE `id` > 0 AND `status` = 0" + query += " AND `" + dependencyName + "` = ?;" + + var categoryEntities CategoryEntities + if err := repository.database.Query(func(cursor ICursor) error { + var ( + id int64 + categoryTypeId int64 + categoryId int64 + title string + description string + ) + + if err := cursor.Scan(&id, &categoryTypeId, &categoryId, &title, &description); err != nil { + return err + } + + categoryEntities = append(categoryEntities, NewCategoryEntity(id, categoryTypeId, categoryId, title, description)) + return nil + }, query, dependencyId); err != nil { + return nil, err + } + + return categoryEntities, nil +} + +func (repository *categoriesRepository) UpdateTitle(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `categories` SET `title` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *categoriesRepository) UpdateTitleAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `categories` SET `title` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *categoriesRepository) UpdateDescription(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `categories` SET `description` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *categoriesRepository) UpdateDescriptionAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `categories` SET `description` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} diff --git a/greataped/components/model/repository/categories_repository_test.go b/greataped/components/model/repository/categories_repository_test.go new file mode 100644 index 0000000..ee06730 --- /dev/null +++ b/greataped/components/model/repository/categories_repository_test.go @@ -0,0 +1,409 @@ +package repository_test + +import ( + "testing" + + . "rail.town/infrastructure/components/model/entity" + . "rail.town/infrastructure/components/model/repository" +) + +func TestCategoriesRepository_Add(test *testing.T) { + type arguments struct { + id int64 + categoryTypeId int64 + categoryId int64 + title string + description string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + categoryTypeId: 0, + categoryId: 0, + title: "title", + description: "description", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + categoryTypeId: 0, + categoryId: 0, + title: "title", + description: "description", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + categoryTypeId: 0, + categoryId: 0, + title: "title", + description: "description", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewCategoryEntity(testCase.arguments.id, testCase.arguments.categoryTypeId, testCase.arguments.categoryId, testCase.arguments.title, testCase.arguments.description) + if result := Categories.Add(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Categories.Add() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestCategoriesRepository_FetchById(test *testing.T) { + type arguments struct { + id int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity, err := Categories.FetchById(testCase.arguments.id) + if result := err == nil; result != testCase.expectation { + test.Errorf("Categories.FetchById() = %v, expected %v", result, testCase.expectation) + } + + _ = entity + }) + } +} + +func TestCategoriesRepository_Update(test *testing.T) { + type arguments struct { + id int64 + categoryTypeId int64 + categoryId int64 + title string + description string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + categoryTypeId: 0, + categoryId: 0, + title: "title", + description: "description", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + categoryTypeId: 0, + categoryId: 0, + title: "title", + description: "description", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + categoryTypeId: 0, + categoryId: 0, + title: "title", + description: "description", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewCategoryEntity(testCase.arguments.id, testCase.arguments.categoryTypeId, testCase.arguments.categoryId, testCase.arguments.title, testCase.arguments.description) + if result := Categories.Update(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Categories.Update() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestCategoriesRepository_Remove(test *testing.T) { + type arguments struct { + id int64 + categoryTypeId int64 + categoryId int64 + title string + description string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewCategoryEntity(testCase.arguments.id, testCase.arguments.categoryTypeId, testCase.arguments.categoryId, testCase.arguments.title, testCase.arguments.description) + if result := Categories.Remove(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Categories.Remove() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestCategoriesRepository_FetchAll(test *testing.T) { + entities, err := Categories.FetchAll() + if err != nil { + test.Fatal(err) + } + + _ = entities +} + +func TestCategoriesRepository_FetchAllByCategoryType(test *testing.T) { + type arguments struct { + categoryTypeId int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + categoryTypeId: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + categoryTypeId: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + categoryTypeId: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entities, err := Categories.FetchAllByCategoryType(testCase.arguments.categoryTypeId) + if result := err == nil; result != testCase.expectation { + test.Errorf("Categories.FetchAllByCategoryType() = %v, expected %v", result, testCase.expectation) + } + + _ = entities + }) + } +} + +func TestCategoriesRepository_FetchAllByCategory(test *testing.T) { + type arguments struct { + categoryId int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + categoryId: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + categoryId: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + categoryId: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entities, err := Categories.FetchAllByCategory(testCase.arguments.categoryId) + if result := err == nil; result != testCase.expectation { + test.Errorf("Categories.FetchAllByCategory() = %v, expected %v", result, testCase.expectation) + } + + _ = entities + }) + } +} + +func TestCategoriesRepository_UpdateTitle(test *testing.T) { + type arguments struct { + id int64 + title string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + title: "title", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + title: "title", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + title: "title", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Categories.UpdateTitle(testCase.arguments.id, testCase.arguments.title, -1) == nil; result != testCase.expectation { + test.Errorf("Categories.UpdateTitle() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestCategoriesRepository_UpdateDescription(test *testing.T) { + type arguments struct { + id int64 + description string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Categories.UpdateDescription(testCase.arguments.id, testCase.arguments.description, -1) == nil; result != testCase.expectation { + test.Errorf("Categories.UpdateDescription() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/components/model/repository/category_types_repository.go b/greataped/components/model/repository/category_types_repository.go new file mode 100644 index 0000000..c6ba99a --- /dev/null +++ b/greataped/components/model/repository/category_types_repository.go @@ -0,0 +1,151 @@ +package repository + +import ( + . "github.com/xeronith/diamante/contracts/database" + . "github.com/xeronith/diamante/contracts/logging" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" +) + +type categoryTypesRepository struct { + baseRepository +} + +func newCategoryTypesRepository(logger ILogger) ICategoryTypesRepository { + return &categoryTypesRepository{ + baseRepository: newBaseRepository("category_type", "category_types", CategoryTypeEntityType, logger, false), + } +} + +func (repository *categoryTypesRepository) Add(entity ICategoryTypeEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `category_types` (`id`, `description`, `editor`) VALUES (?, ?, ?);" + return repository.database.InsertSingle(query, entity.Id(), entity.Description(), editor) +} + +func (repository *categoryTypesRepository) AddAtomic(transaction IRepositoryTransaction, entity ICategoryTypeEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `category_types` (`id`, `description`, `editor`) VALUES (?, ?, ?);" + return repository.database.InsertSingleAtomic(transaction, query, entity.Id(), entity.Description(), editor) +} + +func (repository *categoryTypesRepository) FetchById(id int64) (ICategoryTypeEntity, error) { + if id <= 0 { + return nil, ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "SELECT `id`, `description` FROM `category_types` WHERE `id` = ? AND `status` = 0;" + + var categoryTypeEntity ICategoryTypeEntity + if err := repository.database.QuerySingle(func(cursor ICursor) error { + var ( + id int64 + description string + ) + + if err := cursor.Scan(&id, &description); err != nil { + return err + } + + categoryTypeEntity = NewCategoryTypeEntity(id, description) + return nil + }, query, id); err != nil { + return nil, err + } + + return categoryTypeEntity, nil +} + +func (repository *categoryTypesRepository) Update(entity ICategoryTypeEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `category_types` SET `description` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, entity.Description(), editor, entity.Id()) +} + +func (repository *categoryTypesRepository) UpdateAtomic(transaction IRepositoryTransaction, entity ICategoryTypeEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `category_types` SET `description` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, entity.Description(), editor, entity.Id()) +} + +func (repository *categoryTypesRepository) Remove(entity ICategoryTypeEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `category_types` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingle(query, editor, entity.Id()) +} + +func (repository *categoryTypesRepository) RemoveAtomic(transaction IRepositoryTransaction, entity ICategoryTypeEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `category_types` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingleAtomic(transaction, query, editor, entity.Id()) +} + +func (repository *categoryTypesRepository) FetchAll() (CategoryTypeEntities, error) { + // language=SQL + query := "SELECT `id`, `description` FROM `category_types` WHERE `id` > 0 AND `status` = 0;" + + var categoryTypeEntities CategoryTypeEntities + if err := repository.database.Query(func(cursor ICursor) error { + var ( + id int64 + description string + ) + + if err := cursor.Scan(&id, &description); err != nil { + return err + } + + categoryTypeEntities = append(categoryTypeEntities, NewCategoryTypeEntity(id, description)) + return nil + }, query); err != nil { + return nil, err + } + + return categoryTypeEntities, nil +} + +func (repository *categoryTypesRepository) UpdateDescription(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `category_types` SET `description` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *categoryTypesRepository) UpdateDescriptionAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `category_types` SET `description` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} diff --git a/greataped/components/model/repository/category_types_repository_test.go b/greataped/components/model/repository/category_types_repository_test.go new file mode 100644 index 0000000..9c6b55c --- /dev/null +++ b/greataped/components/model/repository/category_types_repository_test.go @@ -0,0 +1,246 @@ +package repository_test + +import ( + "testing" + + . "rail.town/infrastructure/components/model/entity" + . "rail.town/infrastructure/components/model/repository" +) + +func TestCategoryTypesRepository_Add(test *testing.T) { + type arguments struct { + id int64 + description string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewCategoryTypeEntity(testCase.arguments.id, testCase.arguments.description) + if result := CategoryTypes.Add(entity, -1) == nil; result != testCase.expectation { + test.Errorf("CategoryTypes.Add() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestCategoryTypesRepository_FetchById(test *testing.T) { + type arguments struct { + id int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity, err := CategoryTypes.FetchById(testCase.arguments.id) + if result := err == nil; result != testCase.expectation { + test.Errorf("CategoryTypes.FetchById() = %v, expected %v", result, testCase.expectation) + } + + _ = entity + }) + } +} + +func TestCategoryTypesRepository_Update(test *testing.T) { + type arguments struct { + id int64 + description string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewCategoryTypeEntity(testCase.arguments.id, testCase.arguments.description) + if result := CategoryTypes.Update(entity, -1) == nil; result != testCase.expectation { + test.Errorf("CategoryTypes.Update() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestCategoryTypesRepository_Remove(test *testing.T) { + type arguments struct { + id int64 + description string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewCategoryTypeEntity(testCase.arguments.id, testCase.arguments.description) + if result := CategoryTypes.Remove(entity, -1) == nil; result != testCase.expectation { + test.Errorf("CategoryTypes.Remove() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestCategoryTypesRepository_FetchAll(test *testing.T) { + entities, err := CategoryTypes.FetchAll() + if err != nil { + test.Fatal(err) + } + + _ = entities +} + +func TestCategoryTypesRepository_UpdateDescription(test *testing.T) { + type arguments struct { + id int64 + description string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + description: "description", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := CategoryTypes.UpdateDescription(testCase.arguments.id, testCase.arguments.description, -1) == nil; result != testCase.expectation { + test.Errorf("CategoryTypes.UpdateDescription() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/components/model/repository/documents_repository.go b/greataped/components/model/repository/documents_repository.go new file mode 100644 index 0000000..64adce9 --- /dev/null +++ b/greataped/components/model/repository/documents_repository.go @@ -0,0 +1,151 @@ +package repository + +import ( + . "github.com/xeronith/diamante/contracts/database" + . "github.com/xeronith/diamante/contracts/logging" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" +) + +type documentsRepository struct { + baseRepository +} + +func newDocumentsRepository(logger ILogger) IDocumentsRepository { + return &documentsRepository{ + baseRepository: newBaseRepository("document", "documents", DocumentEntityType, logger, false), + } +} + +func (repository *documentsRepository) Add(entity IDocumentEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `documents` (`id`, `content`, `editor`) VALUES (?, ?, ?);" + return repository.database.InsertSingle(query, entity.Id(), entity.Content(), editor) +} + +func (repository *documentsRepository) AddAtomic(transaction IRepositoryTransaction, entity IDocumentEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `documents` (`id`, `content`, `editor`) VALUES (?, ?, ?);" + return repository.database.InsertSingleAtomic(transaction, query, entity.Id(), entity.Content(), editor) +} + +func (repository *documentsRepository) FetchById(id int64) (IDocumentEntity, error) { + if id <= 0 { + return nil, ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "SELECT `id`, `content` FROM `documents` WHERE `id` = ? AND `status` = 0;" + + var documentEntity IDocumentEntity + if err := repository.database.QuerySingle(func(cursor ICursor) error { + var ( + id int64 + content string + ) + + if err := cursor.Scan(&id, &content); err != nil { + return err + } + + documentEntity = NewDocumentEntity(id, content) + return nil + }, query, id); err != nil { + return nil, err + } + + return documentEntity, nil +} + +func (repository *documentsRepository) Update(entity IDocumentEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `documents` SET `content` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, entity.Content(), editor, entity.Id()) +} + +func (repository *documentsRepository) UpdateAtomic(transaction IRepositoryTransaction, entity IDocumentEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `documents` SET `content` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, entity.Content(), editor, entity.Id()) +} + +func (repository *documentsRepository) Remove(entity IDocumentEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `documents` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingle(query, editor, entity.Id()) +} + +func (repository *documentsRepository) RemoveAtomic(transaction IRepositoryTransaction, entity IDocumentEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `documents` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingleAtomic(transaction, query, editor, entity.Id()) +} + +func (repository *documentsRepository) FetchAll() (DocumentEntities, error) { + // language=SQL + query := "SELECT `id`, `content` FROM `documents` WHERE `id` > 0 AND `status` = 0;" + + var documentEntities DocumentEntities + if err := repository.database.Query(func(cursor ICursor) error { + var ( + id int64 + content string + ) + + if err := cursor.Scan(&id, &content); err != nil { + return err + } + + documentEntities = append(documentEntities, NewDocumentEntity(id, content)) + return nil + }, query); err != nil { + return nil, err + } + + return documentEntities, nil +} + +func (repository *documentsRepository) UpdateContent(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `documents` SET `content` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *documentsRepository) UpdateContentAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `documents` SET `content` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} diff --git a/greataped/components/model/repository/documents_repository_test.go b/greataped/components/model/repository/documents_repository_test.go new file mode 100644 index 0000000..d734139 --- /dev/null +++ b/greataped/components/model/repository/documents_repository_test.go @@ -0,0 +1,246 @@ +package repository_test + +import ( + "testing" + + . "rail.town/infrastructure/components/model/entity" + . "rail.town/infrastructure/components/model/repository" +) + +func TestDocumentsRepository_Add(test *testing.T) { + type arguments struct { + id int64 + content string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + content: "content", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + content: "content", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + content: "content", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewDocumentEntity(testCase.arguments.id, testCase.arguments.content) + if result := Documents.Add(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Documents.Add() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestDocumentsRepository_FetchById(test *testing.T) { + type arguments struct { + id int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity, err := Documents.FetchById(testCase.arguments.id) + if result := err == nil; result != testCase.expectation { + test.Errorf("Documents.FetchById() = %v, expected %v", result, testCase.expectation) + } + + _ = entity + }) + } +} + +func TestDocumentsRepository_Update(test *testing.T) { + type arguments struct { + id int64 + content string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + content: "content", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + content: "content", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + content: "content", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewDocumentEntity(testCase.arguments.id, testCase.arguments.content) + if result := Documents.Update(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Documents.Update() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestDocumentsRepository_Remove(test *testing.T) { + type arguments struct { + id int64 + content string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewDocumentEntity(testCase.arguments.id, testCase.arguments.content) + if result := Documents.Remove(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Documents.Remove() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestDocumentsRepository_FetchAll(test *testing.T) { + entities, err := Documents.FetchAll() + if err != nil { + test.Fatal(err) + } + + _ = entities +} + +func TestDocumentsRepository_UpdateContent(test *testing.T) { + type arguments struct { + id int64 + content string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + content: "content", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + content: "content", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + content: "content", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Documents.UpdateContent(testCase.arguments.id, testCase.arguments.content, -1) == nil; result != testCase.expectation { + test.Errorf("Documents.UpdateContent() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/components/model/repository/identities_repository.go b/greataped/components/model/repository/identities_repository.go new file mode 100644 index 0000000..af08153 --- /dev/null +++ b/greataped/components/model/repository/identities_repository.go @@ -0,0 +1,591 @@ +package repository + +import ( + . "github.com/xeronith/diamante/contracts/database" + . "github.com/xeronith/diamante/contracts/logging" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" +) + +type identitiesRepository struct { + baseRepository +} + +func newIdentitiesRepository(logger ILogger) IIdentitiesRepository { + return &identitiesRepository{ + baseRepository: newBaseRepository("identity", "identities", IdentityEntityType, logger, false), + } +} + +func (repository *identitiesRepository) Add(entity IIdentityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `identities` (`id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" + return repository.database.InsertSingle(query, entity.Id(), entity.Username(), entity.PhoneNumber(), entity.PhoneNumberConfirmed(), entity.FirstName(), entity.LastName(), entity.DisplayName(), entity.Email(), entity.EmailConfirmed(), entity.Avatar(), entity.Banner(), entity.Summary(), entity.Token(), entity.MultiFactor(), entity.Hash(), entity.Salt(), entity.PublicKey(), entity.PrivateKey(), entity.Permission(), entity.Restriction(), entity.LastLogin(), entity.LoginCount(), editor) +} + +func (repository *identitiesRepository) AddAtomic(transaction IRepositoryTransaction, entity IIdentityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `identities` (`id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" + return repository.database.InsertSingleAtomic(transaction, query, entity.Id(), entity.Username(), entity.PhoneNumber(), entity.PhoneNumberConfirmed(), entity.FirstName(), entity.LastName(), entity.DisplayName(), entity.Email(), entity.EmailConfirmed(), entity.Avatar(), entity.Banner(), entity.Summary(), entity.Token(), entity.MultiFactor(), entity.Hash(), entity.Salt(), entity.PublicKey(), entity.PrivateKey(), entity.Permission(), entity.Restriction(), entity.LastLogin(), entity.LoginCount(), editor) +} + +func (repository *identitiesRepository) FetchById(id int64) (IIdentityEntity, error) { + if id <= 0 { + return nil, ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "SELECT `id`, `username`, `phone_number`, `phone_number_confirmed` = b'1', `first_name`, `last_name`, `display_name`, `email`, `email_confirmed` = b'1', `avatar`, `banner`, `summary`, `token`, `multi_factor` = b'1', `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count` FROM `identities` WHERE `id` = ? AND `status` = 0;" + + var identityEntity IIdentityEntity + if err := repository.database.QuerySingle(func(cursor ICursor) error { + var ( + id int64 + username string + phoneNumber string + phoneNumberConfirmed bool + firstName string + lastName string + displayName string + email string + emailConfirmed bool + avatar string + banner string + summary string + token string + multiFactor bool + hash string + salt string + publicKey string + privateKey string + permission uint64 + restriction uint32 + lastLogin int64 + loginCount uint32 + ) + + if err := cursor.Scan(&id, &username, &phoneNumber, &phoneNumberConfirmed, &firstName, &lastName, &displayName, &email, &emailConfirmed, &avatar, &banner, &summary, &token, &multiFactor, &hash, &salt, &publicKey, &privateKey, &permission, &restriction, &lastLogin, &loginCount); err != nil { + return err + } + + identityEntity = NewIdentityEntity(id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount) + return nil + }, query, id); err != nil { + return nil, err + } + + return identityEntity, nil +} + +func (repository *identitiesRepository) Update(entity IIdentityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `username` = ?, `phone_number` = ?, `phone_number_confirmed` = ?, `first_name` = ?, `last_name` = ?, `display_name` = ?, `email` = ?, `email_confirmed` = ?, `avatar` = ?, `banner` = ?, `summary` = ?, `token` = ?, `multi_factor` = ?, `hash` = ?, `salt` = ?, `public_key` = ?, `private_key` = ?, `permission` = ?, `restriction` = ?, `last_login` = ?, `login_count` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, entity.Username(), entity.PhoneNumber(), entity.PhoneNumberConfirmed(), entity.FirstName(), entity.LastName(), entity.DisplayName(), entity.Email(), entity.EmailConfirmed(), entity.Avatar(), entity.Banner(), entity.Summary(), entity.Token(), entity.MultiFactor(), entity.Hash(), entity.Salt(), entity.PublicKey(), entity.PrivateKey(), entity.Permission(), entity.Restriction(), entity.LastLogin(), entity.LoginCount(), editor, entity.Id()) +} + +func (repository *identitiesRepository) UpdateAtomic(transaction IRepositoryTransaction, entity IIdentityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `username` = ?, `phone_number` = ?, `phone_number_confirmed` = ?, `first_name` = ?, `last_name` = ?, `display_name` = ?, `email` = ?, `email_confirmed` = ?, `avatar` = ?, `banner` = ?, `summary` = ?, `token` = ?, `multi_factor` = ?, `hash` = ?, `salt` = ?, `public_key` = ?, `private_key` = ?, `permission` = ?, `restriction` = ?, `last_login` = ?, `login_count` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, entity.Username(), entity.PhoneNumber(), entity.PhoneNumberConfirmed(), entity.FirstName(), entity.LastName(), entity.DisplayName(), entity.Email(), entity.EmailConfirmed(), entity.Avatar(), entity.Banner(), entity.Summary(), entity.Token(), entity.MultiFactor(), entity.Hash(), entity.Salt(), entity.PublicKey(), entity.PrivateKey(), entity.Permission(), entity.Restriction(), entity.LastLogin(), entity.LoginCount(), editor, entity.Id()) +} + +func (repository *identitiesRepository) Remove(entity IIdentityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingle(query, editor, entity.Id()) +} + +func (repository *identitiesRepository) RemoveAtomic(transaction IRepositoryTransaction, entity IIdentityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingleAtomic(transaction, query, editor, entity.Id()) +} + +func (repository *identitiesRepository) FetchAll() (IdentityEntities, error) { + // language=SQL + query := "SELECT `id`, `username`, `phone_number`, `phone_number_confirmed` = b'1', `first_name`, `last_name`, `display_name`, `email`, `email_confirmed` = b'1', `avatar`, `banner`, `summary`, `token`, `multi_factor` = b'1', `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count` FROM `identities` WHERE `id` > 0 AND `status` = 0;" + + var identityEntities IdentityEntities + if err := repository.database.Query(func(cursor ICursor) error { + var ( + id int64 + username string + phoneNumber string + phoneNumberConfirmed bool + firstName string + lastName string + displayName string + email string + emailConfirmed bool + avatar string + banner string + summary string + token string + multiFactor bool + hash string + salt string + publicKey string + privateKey string + permission uint64 + restriction uint32 + lastLogin int64 + loginCount uint32 + ) + + if err := cursor.Scan(&id, &username, &phoneNumber, &phoneNumberConfirmed, &firstName, &lastName, &displayName, &email, &emailConfirmed, &avatar, &banner, &summary, &token, &multiFactor, &hash, &salt, &publicKey, &privateKey, &permission, &restriction, &lastLogin, &loginCount); err != nil { + return err + } + + identityEntities = append(identityEntities, NewIdentityEntity(id, username, phoneNumber, phoneNumberConfirmed, firstName, lastName, displayName, email, emailConfirmed, avatar, banner, summary, token, multiFactor, hash, salt, publicKey, privateKey, permission, restriction, lastLogin, loginCount)) + return nil + }, query); err != nil { + return nil, err + } + + return identityEntities, nil +} + +func (repository *identitiesRepository) UpdateUsername(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `username` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateUsernameAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `username` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdatePhoneNumber(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `phone_number` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdatePhoneNumberAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `phone_number` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdatePhoneNumberConfirmed(id int64, value bool, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `phone_number_confirmed` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdatePhoneNumberConfirmedAtomic(transaction IRepositoryTransaction, id int64, value bool, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `phone_number_confirmed` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateFirstName(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `first_name` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateFirstNameAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `first_name` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateLastName(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `last_name` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateLastNameAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `last_name` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateDisplayName(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `display_name` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateDisplayNameAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `display_name` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateEmail(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `email` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateEmailAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `email` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateEmailConfirmed(id int64, value bool, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `email_confirmed` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateEmailConfirmedAtomic(transaction IRepositoryTransaction, id int64, value bool, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `email_confirmed` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateAvatar(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `avatar` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateAvatarAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `avatar` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateBanner(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `banner` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateBannerAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `banner` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateSummary(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `summary` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateSummaryAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `summary` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateToken(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `token` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateTokenAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `token` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateMultiFactor(id int64, value bool, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `multi_factor` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateMultiFactorAtomic(transaction IRepositoryTransaction, id int64, value bool, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `multi_factor` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateHash(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `hash` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateHashAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `hash` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateSalt(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `salt` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateSaltAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `salt` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdatePublicKey(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `public_key` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdatePublicKeyAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `public_key` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdatePrivateKey(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `private_key` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdatePrivateKeyAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `private_key` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdatePermission(id int64, value uint64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `permission` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdatePermissionAtomic(transaction IRepositoryTransaction, id int64, value uint64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `permission` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateRestriction(id int64, value uint32, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `restriction` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateRestrictionAtomic(transaction IRepositoryTransaction, id int64, value uint32, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `restriction` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateLastLogin(id int64, value int64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `last_login` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateLastLoginAtomic(transaction IRepositoryTransaction, id int64, value int64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `last_login` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateLoginCount(id int64, value uint32, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `login_count` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *identitiesRepository) UpdateLoginCountAtomic(transaction IRepositoryTransaction, id int64, value uint32, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `identities` SET `login_count` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} diff --git a/greataped/components/model/repository/identities_repository_test.go b/greataped/components/model/repository/identities_repository_test.go new file mode 100644 index 0000000..d54b80f --- /dev/null +++ b/greataped/components/model/repository/identities_repository_test.go @@ -0,0 +1,1346 @@ +package repository_test + +import ( + "testing" + + . "rail.town/infrastructure/components/model/entity" + . "rail.town/infrastructure/components/model/repository" +) + +func TestIdentitiesRepository_Add(test *testing.T) { + type arguments struct { + id int64 + username string + phoneNumber string + phoneNumberConfirmed bool + firstName string + lastName string + displayName string + email string + emailConfirmed bool + avatar string + banner string + summary string + token string + multiFactor bool + hash string + salt string + publicKey string + privateKey string + permission uint64 + restriction uint32 + lastLogin int64 + loginCount uint32 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + username: "username", + phoneNumber: "phone_number", + phoneNumberConfirmed: true, + firstName: "first_name", + lastName: "last_name", + displayName: "display_name", + email: "email", + emailConfirmed: true, + avatar: "avatar", + banner: "banner", + summary: "summary", + token: "token", + multiFactor: true, + hash: "hash", + salt: "salt", + publicKey: "public_key", + privateKey: "private_key", + permission: 0, + restriction: 0, + lastLogin: 0, + loginCount: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + username: "username", + phoneNumber: "phone_number", + phoneNumberConfirmed: true, + firstName: "first_name", + lastName: "last_name", + displayName: "display_name", + email: "email", + emailConfirmed: true, + avatar: "avatar", + banner: "banner", + summary: "summary", + token: "token", + multiFactor: true, + hash: "hash", + salt: "salt", + publicKey: "public_key", + privateKey: "private_key", + permission: 0, + restriction: 0, + lastLogin: 0, + loginCount: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + username: "username", + phoneNumber: "phone_number", + phoneNumberConfirmed: true, + firstName: "first_name", + lastName: "last_name", + displayName: "display_name", + email: "email", + emailConfirmed: true, + avatar: "avatar", + banner: "banner", + summary: "summary", + token: "token", + multiFactor: true, + hash: "hash", + salt: "salt", + publicKey: "public_key", + privateKey: "private_key", + permission: 0, + restriction: 0, + lastLogin: 0, + loginCount: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewIdentityEntity(testCase.arguments.id, testCase.arguments.username, testCase.arguments.phoneNumber, testCase.arguments.phoneNumberConfirmed, testCase.arguments.firstName, testCase.arguments.lastName, testCase.arguments.displayName, testCase.arguments.email, testCase.arguments.emailConfirmed, testCase.arguments.avatar, testCase.arguments.banner, testCase.arguments.summary, testCase.arguments.token, testCase.arguments.multiFactor, testCase.arguments.hash, testCase.arguments.salt, testCase.arguments.publicKey, testCase.arguments.privateKey, testCase.arguments.permission, testCase.arguments.restriction, testCase.arguments.lastLogin, testCase.arguments.loginCount) + if result := Identities.Add(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.Add() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_FetchById(test *testing.T) { + type arguments struct { + id int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity, err := Identities.FetchById(testCase.arguments.id) + if result := err == nil; result != testCase.expectation { + test.Errorf("Identities.FetchById() = %v, expected %v", result, testCase.expectation) + } + + _ = entity + }) + } +} + +func TestIdentitiesRepository_Update(test *testing.T) { + type arguments struct { + id int64 + username string + phoneNumber string + phoneNumberConfirmed bool + firstName string + lastName string + displayName string + email string + emailConfirmed bool + avatar string + banner string + summary string + token string + multiFactor bool + hash string + salt string + publicKey string + privateKey string + permission uint64 + restriction uint32 + lastLogin int64 + loginCount uint32 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + username: "username", + phoneNumber: "phone_number", + phoneNumberConfirmed: true, + firstName: "first_name", + lastName: "last_name", + displayName: "display_name", + email: "email", + emailConfirmed: true, + avatar: "avatar", + banner: "banner", + summary: "summary", + token: "token", + multiFactor: true, + hash: "hash", + salt: "salt", + publicKey: "public_key", + privateKey: "private_key", + permission: 0, + restriction: 0, + lastLogin: 0, + loginCount: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + username: "username", + phoneNumber: "phone_number", + phoneNumberConfirmed: true, + firstName: "first_name", + lastName: "last_name", + displayName: "display_name", + email: "email", + emailConfirmed: true, + avatar: "avatar", + banner: "banner", + summary: "summary", + token: "token", + multiFactor: true, + hash: "hash", + salt: "salt", + publicKey: "public_key", + privateKey: "private_key", + permission: 0, + restriction: 0, + lastLogin: 0, + loginCount: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + username: "username", + phoneNumber: "phone_number", + phoneNumberConfirmed: true, + firstName: "first_name", + lastName: "last_name", + displayName: "display_name", + email: "email", + emailConfirmed: true, + avatar: "avatar", + banner: "banner", + summary: "summary", + token: "token", + multiFactor: true, + hash: "hash", + salt: "salt", + publicKey: "public_key", + privateKey: "private_key", + permission: 0, + restriction: 0, + lastLogin: 0, + loginCount: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewIdentityEntity(testCase.arguments.id, testCase.arguments.username, testCase.arguments.phoneNumber, testCase.arguments.phoneNumberConfirmed, testCase.arguments.firstName, testCase.arguments.lastName, testCase.arguments.displayName, testCase.arguments.email, testCase.arguments.emailConfirmed, testCase.arguments.avatar, testCase.arguments.banner, testCase.arguments.summary, testCase.arguments.token, testCase.arguments.multiFactor, testCase.arguments.hash, testCase.arguments.salt, testCase.arguments.publicKey, testCase.arguments.privateKey, testCase.arguments.permission, testCase.arguments.restriction, testCase.arguments.lastLogin, testCase.arguments.loginCount) + if result := Identities.Update(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.Update() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_Remove(test *testing.T) { + type arguments struct { + id int64 + username string + phoneNumber string + phoneNumberConfirmed bool + firstName string + lastName string + displayName string + email string + emailConfirmed bool + avatar string + banner string + summary string + token string + multiFactor bool + hash string + salt string + publicKey string + privateKey string + permission uint64 + restriction uint32 + lastLogin int64 + loginCount uint32 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewIdentityEntity(testCase.arguments.id, testCase.arguments.username, testCase.arguments.phoneNumber, testCase.arguments.phoneNumberConfirmed, testCase.arguments.firstName, testCase.arguments.lastName, testCase.arguments.displayName, testCase.arguments.email, testCase.arguments.emailConfirmed, testCase.arguments.avatar, testCase.arguments.banner, testCase.arguments.summary, testCase.arguments.token, testCase.arguments.multiFactor, testCase.arguments.hash, testCase.arguments.salt, testCase.arguments.publicKey, testCase.arguments.privateKey, testCase.arguments.permission, testCase.arguments.restriction, testCase.arguments.lastLogin, testCase.arguments.loginCount) + if result := Identities.Remove(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.Remove() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_FetchAll(test *testing.T) { + entities, err := Identities.FetchAll() + if err != nil { + test.Fatal(err) + } + + _ = entities +} + +func TestIdentitiesRepository_UpdateUsername(test *testing.T) { + type arguments struct { + id int64 + username string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + username: "username", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + username: "username", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + username: "username", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateUsername(testCase.arguments.id, testCase.arguments.username, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateUsername() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdatePhoneNumber(test *testing.T) { + type arguments struct { + id int64 + phoneNumber string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + phoneNumber: "phone_number", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + phoneNumber: "phone_number", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + phoneNumber: "phone_number", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdatePhoneNumber(testCase.arguments.id, testCase.arguments.phoneNumber, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdatePhoneNumber() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdatePhoneNumberConfirmed(test *testing.T) { + type arguments struct { + id int64 + phoneNumberConfirmed bool + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + phoneNumberConfirmed: true, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + phoneNumberConfirmed: true, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + phoneNumberConfirmed: true, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdatePhoneNumberConfirmed(testCase.arguments.id, testCase.arguments.phoneNumberConfirmed, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdatePhoneNumberConfirmed() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateFirstName(test *testing.T) { + type arguments struct { + id int64 + firstName string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + firstName: "first_name", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + firstName: "first_name", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + firstName: "first_name", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateFirstName(testCase.arguments.id, testCase.arguments.firstName, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateFirstName() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateLastName(test *testing.T) { + type arguments struct { + id int64 + lastName string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + lastName: "last_name", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + lastName: "last_name", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + lastName: "last_name", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateLastName(testCase.arguments.id, testCase.arguments.lastName, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateLastName() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateDisplayName(test *testing.T) { + type arguments struct { + id int64 + displayName string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + displayName: "display_name", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + displayName: "display_name", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + displayName: "display_name", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateDisplayName(testCase.arguments.id, testCase.arguments.displayName, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateDisplayName() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateEmail(test *testing.T) { + type arguments struct { + id int64 + email string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + email: "email", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + email: "email", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + email: "email", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateEmail(testCase.arguments.id, testCase.arguments.email, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateEmail() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateEmailConfirmed(test *testing.T) { + type arguments struct { + id int64 + emailConfirmed bool + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + emailConfirmed: true, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + emailConfirmed: true, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + emailConfirmed: true, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateEmailConfirmed(testCase.arguments.id, testCase.arguments.emailConfirmed, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateEmailConfirmed() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateAvatar(test *testing.T) { + type arguments struct { + id int64 + avatar string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + avatar: "avatar", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + avatar: "avatar", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + avatar: "avatar", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateAvatar(testCase.arguments.id, testCase.arguments.avatar, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateAvatar() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateBanner(test *testing.T) { + type arguments struct { + id int64 + banner string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + banner: "banner", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + banner: "banner", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + banner: "banner", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateBanner(testCase.arguments.id, testCase.arguments.banner, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateBanner() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateSummary(test *testing.T) { + type arguments struct { + id int64 + summary string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + summary: "summary", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + summary: "summary", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + summary: "summary", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateSummary(testCase.arguments.id, testCase.arguments.summary, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateSummary() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateToken(test *testing.T) { + type arguments struct { + id int64 + token string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + token: "token", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + token: "token", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + token: "token", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateToken(testCase.arguments.id, testCase.arguments.token, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateToken() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateMultiFactor(test *testing.T) { + type arguments struct { + id int64 + multiFactor bool + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + multiFactor: true, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + multiFactor: true, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + multiFactor: true, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateMultiFactor(testCase.arguments.id, testCase.arguments.multiFactor, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateMultiFactor() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateHash(test *testing.T) { + type arguments struct { + id int64 + hash string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + hash: "hash", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + hash: "hash", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + hash: "hash", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateHash(testCase.arguments.id, testCase.arguments.hash, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateHash() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateSalt(test *testing.T) { + type arguments struct { + id int64 + salt string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + salt: "salt", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + salt: "salt", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + salt: "salt", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateSalt(testCase.arguments.id, testCase.arguments.salt, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateSalt() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdatePublicKey(test *testing.T) { + type arguments struct { + id int64 + publicKey string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + publicKey: "public_key", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + publicKey: "public_key", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + publicKey: "public_key", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdatePublicKey(testCase.arguments.id, testCase.arguments.publicKey, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdatePublicKey() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdatePrivateKey(test *testing.T) { + type arguments struct { + id int64 + privateKey string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + privateKey: "private_key", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + privateKey: "private_key", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + privateKey: "private_key", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdatePrivateKey(testCase.arguments.id, testCase.arguments.privateKey, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdatePrivateKey() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdatePermission(test *testing.T) { + type arguments struct { + id int64 + permission uint64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + permission: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + permission: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + permission: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdatePermission(testCase.arguments.id, testCase.arguments.permission, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdatePermission() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateRestriction(test *testing.T) { + type arguments struct { + id int64 + restriction uint32 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + restriction: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + restriction: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + restriction: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateRestriction(testCase.arguments.id, testCase.arguments.restriction, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateRestriction() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateLastLogin(test *testing.T) { + type arguments struct { + id int64 + lastLogin int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + lastLogin: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + lastLogin: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + lastLogin: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateLastLogin(testCase.arguments.id, testCase.arguments.lastLogin, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateLastLogin() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestIdentitiesRepository_UpdateLoginCount(test *testing.T) { + type arguments struct { + id int64 + loginCount uint32 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + loginCount: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + loginCount: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + loginCount: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Identities.UpdateLoginCount(testCase.arguments.id, testCase.arguments.loginCount, -1) == nil; result != testCase.expectation { + test.Errorf("Identities.UpdateLoginCount() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/components/model/repository/initializer.go b/greataped/components/model/repository/initializer.go new file mode 100644 index 0000000..88f7803 --- /dev/null +++ b/greataped/components/model/repository/initializer.go @@ -0,0 +1,79 @@ +package repository + +import ( + . "github.com/xeronith/diamante/contracts/database" + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/settings" + . "github.com/xeronith/diamante/database/drivers/mysql" + . "rail.town/infrastructure/components/contracts/model" +) + +var ( + Pipe IPipeRepository + Documents IDocumentsRepository + SystemSchedules ISystemSchedulesRepository + Identities IIdentitiesRepository + AccessControls IAccessControlsRepository + RemoteActivities IRemoteActivitiesRepository + CategoryTypes ICategoryTypesRepository + Categories ICategoriesRepository + Users IUsersRepository +) + +var database ISqlDatabase + +func Initialize(configuration IConfiguration, logger ILogger) error { + databaseName := "sapphire" + if configuration.IsDockerized() { + databaseName = configuration.GetMySQLConfiguration().GetDatabase() + } + + database = NewDatabase(configuration, logger, databaseName) + if err := database.Initialize(); err != nil { + return err + } + + Pipe = newPipeRepository(logger) + Documents = newDocumentsRepository(logger) + SystemSchedules = newSystemSchedulesRepository(logger) + Identities = newIdentitiesRepository(logger) + AccessControls = newAccessControlsRepository(logger) + RemoteActivities = newRemoteActivitiesRepository(logger) + CategoryTypes = newCategoryTypesRepository(logger) + Categories = newCategoriesRepository(logger) + Users = newUsersRepository(logger) + + repositories := []IRepository{ + Pipe, + Documents, + SystemSchedules, + Identities, + AccessControls, + RemoteActivities, + CategoryTypes, + Categories, + Users, + } + + for _, repository := range repositories { + repository.SetSqlDatabase(database) + } + + for _, repository := range repositories { + if err := repository.Migrate(); err != nil { + return err + } + } + + return nil +} + +func WithTransaction(handler RepositoryTransactionHandler) error { + if database == nil { + panic("repository_not_initialized") + } + + return database.WithTransaction(func(transaction ISqlTransaction) error { + return handler(transaction) + }) +} diff --git a/greataped/components/model/repository/pipe.go b/greataped/components/model/repository/pipe.go new file mode 100644 index 0000000..624ffcc --- /dev/null +++ b/greataped/components/model/repository/pipe.go @@ -0,0 +1,103 @@ +package repository + +import ( + "fmt" + "sync" + "time" + + "github.com/xeronith/diamante/logging" + . "rail.town/infrastructure/components/contracts/model" +) + +// noinspection GoSnakeCaseUsage +const ( + BUFFER_LENGTH = int64(1000) // Should be set in regard to `max_allowed_packet` size in MySQL + AUTO_FLUSH_DURATION = time.Millisecond * 100 + DEFAULT_INPUT_CHANNEL_SIZE = 10000 + + SIGNAL_FLUSH = 0 +) + +type pipe struct { + repository IRepository + descriptorId int + input chan IPipeEntity + signal chan int + semaphore *sync.Mutex + query string + parametersResolver func(IPipeEntity) Parameters + cursor int64 + buffer [BUFFER_LENGTH]IPipeEntity + lastFlush time.Time +} + +func NewPipe(descriptor IPipeDescriptor, repository IRepository) IPipe { + return &pipe{ + repository: repository, + descriptorId: descriptor.Id(), + input: make(chan IPipeEntity, DEFAULT_INPUT_CHANNEL_SIZE), + signal: make(chan int), + semaphore: descriptor.GetSemaphore(), + query: descriptor.GetQuery(), + parametersResolver: descriptor.GetParametersResolver(), + lastFlush: time.Date(1970, time.January, 0, 0, 0, 0, 0, time.UTC), + } +} + +func (pipe *pipe) Input() chan IPipeEntity { + return pipe.input +} + +func (pipe *pipe) Signal() chan int { + return pipe.signal +} + +func (pipe *pipe) flush(cursor int64) int64 { + if pipe.cursor == 0 { + return 0 + } + + if cursor < BUFFER_LENGTH && time.Since(pipe.lastFlush) < AUTO_FLUSH_DURATION { + return cursor + } + + pipe.semaphore.Lock() + defer pipe.semaphore.Unlock() + + parameters := make(Parameters, 0) + for index := int64(0); index < cursor; index++ { + parameters = append(parameters, pipe.parametersResolver(pipe.buffer[index])...) + } + + if err := pipe.repository.GetSqlDatabase().InsertAll(pipe.query, cursor, parameters...); err != nil { + pipe.repository.Serialize(pipe.buffer[:cursor], err) + } + + pipe.lastFlush = time.Now() + + return 0 +} + +func (pipe *pipe) OpenValve() { + defer pipe.catch() + + for { + select { + case _entity := <-pipe.Input(): + pipe.buffer[pipe.cursor] = _entity + pipe.cursor++ + + if pipe.cursor == BUFFER_LENGTH { + pipe.cursor = pipe.flush(pipe.cursor) + } + case <-pipe.Signal(): + pipe.cursor = pipe.flush(pipe.cursor) + } + } +} + +func (pipe *pipe) catch() { + if reason := recover(); reason != nil { + logging.GetDefaultLogger().Panic(fmt.Sprintf("PIPE_REPOSITORY: PIPE %d, STORE %s / %s", pipe.descriptorId, pipe.repository.GetSqlDatabase().GetName(), reason)) + } +} diff --git a/greataped/components/model/repository/pipe_descriptor.go b/greataped/components/model/repository/pipe_descriptor.go new file mode 100644 index 0000000..f2771f4 --- /dev/null +++ b/greataped/components/model/repository/pipe_descriptor.go @@ -0,0 +1,30 @@ +package repository + +import ( + "sync" + + . "rail.town/infrastructure/components/contracts/model" +) + +type pipeDescriptor struct { + id int + semaphore *sync.Mutex + query string + parametersResolver func(entity IPipeEntity) Parameters +} + +func (command *pipeDescriptor) Id() int { + return command.id +} + +func (command *pipeDescriptor) GetSemaphore() *sync.Mutex { + return command.semaphore +} + +func (command *pipeDescriptor) GetQuery() string { + return command.query +} + +func (command *pipeDescriptor) GetParametersResolver() func(IPipeEntity) Parameters { + return command.parametersResolver +} diff --git a/greataped/components/model/repository/pipe_repository.go b/greataped/components/model/repository/pipe_repository.go new file mode 100644 index 0000000..3bd6ded --- /dev/null +++ b/greataped/components/model/repository/pipe_repository.go @@ -0,0 +1,129 @@ +package repository + +import ( + "sync" + "time" + + . "github.com/xeronith/diamante/contracts/logging" + . "rail.town/infrastructure/components/contracts/model" +) + +func (repository *pipeRepository) GetPipeDescriptors() []*pipeDescriptor { + descriptors := []*pipeDescriptor{ + { + PIPE_DOCUMENT, + &sync.Mutex{}, + "INSERT INTO `documents` (`id`, `content`, `editor`, `queued_at`, `payload`) VALUES (?, ?, ?, ?, ?);", + func(entity IPipeEntity) Parameters { + e := entity.(IDocumentPipeEntity) + return Parameters{e.Id(), e.Content(), e.GetEditor(), e.GetQueueTimestamp().UnixNano(), e.Payload()} + }, + }, + { + PIPE_SYSTEM_SCHEDULE, + &sync.Mutex{}, + "INSERT INTO `system_schedules` (`id`, `enabled`, `config`, `editor`, `queued_at`, `payload`) VALUES (?, ?, ?, ?, ?, ?);", + func(entity IPipeEntity) Parameters { + e := entity.(ISystemSchedulePipeEntity) + return Parameters{e.Id(), e.Enabled(), e.Config(), e.GetEditor(), e.GetQueueTimestamp().UnixNano(), e.Payload()} + }, + }, + { + PIPE_IDENTITY, + &sync.Mutex{}, + "INSERT INTO `identities` (`id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`, `queued_at`, `payload`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + func(entity IPipeEntity) Parameters { + e := entity.(IIdentityPipeEntity) + return Parameters{e.Id(), e.Username(), e.PhoneNumber(), e.PhoneNumberConfirmed(), e.FirstName(), e.LastName(), e.DisplayName(), e.Email(), e.EmailConfirmed(), e.Avatar(), e.Banner(), e.Summary(), e.Token(), e.MultiFactor(), e.Hash(), e.Salt(), e.PublicKey(), e.PrivateKey(), e.Permission(), e.Restriction(), e.LastLogin(), e.LoginCount(), e.GetEditor(), e.GetQueueTimestamp().UnixNano(), e.Payload()} + }, + }, + { + PIPE_ACCESS_CONTROL, + &sync.Mutex{}, + "INSERT INTO `access_controls` (`id`, `key`, `value`, `editor`, `queued_at`, `payload`) VALUES (?, ?, ?, ?, ?, ?);", + func(entity IPipeEntity) Parameters { + e := entity.(IAccessControlPipeEntity) + return Parameters{e.Id(), e.Key(), e.Value(), e.GetEditor(), e.GetQueueTimestamp().UnixNano(), e.Payload()} + }, + }, + { + PIPE_REMOTE_ACTIVITY, + &sync.Mutex{}, + "INSERT INTO `remote_activities` (`id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`, `queued_at`, `payload`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + func(entity IPipeEntity) Parameters { + e := entity.(IRemoteActivityPipeEntity) + return Parameters{e.Id(), e.EntryPoint(), e.Duration(), e.Successful(), e.ErrorMessage(), e.RemoteAddress(), e.UserAgent(), e.EventType(), e.Timestamp(), e.GetEditor(), e.GetQueueTimestamp().UnixNano(), e.Payload()} + }, + }, + { + PIPE_CATEGORY_TYPE, + &sync.Mutex{}, + "INSERT INTO `category_types` (`id`, `description`, `editor`, `queued_at`, `payload`) VALUES (?, ?, ?, ?, ?);", + func(entity IPipeEntity) Parameters { + e := entity.(ICategoryTypePipeEntity) + return Parameters{e.Id(), e.Description(), e.GetEditor(), e.GetQueueTimestamp().UnixNano(), e.Payload()} + }, + }, + { + PIPE_CATEGORY, + &sync.Mutex{}, + "INSERT INTO `categories` (`id`, `category_type_id`, `category_id`, `title`, `description`, `editor`, `queued_at`, `payload`) VALUES (?, ?, ?, ?, ?, ?, ?, ?);", + func(entity IPipeEntity) Parameters { + e := entity.(ICategoryPipeEntity) + return Parameters{e.Id(), e.CategoryTypeId(), e.CategoryId(), e.Title(), e.Description(), e.GetEditor(), e.GetQueueTimestamp().UnixNano(), e.Payload()} + }, + }, + { + PIPE_USER, + &sync.Mutex{}, + "INSERT INTO `users` (`id`, `github`, `editor`, `queued_at`, `payload`) VALUES (?, ?, ?, ?, ?);", + func(entity IPipeEntity) Parameters { + e := entity.(IUserPipeEntity) + return Parameters{e.Id(), e.Github(), e.GetEditor(), e.GetQueueTimestamp().UnixNano(), e.Payload()} + }, + }, + } + + return descriptors +} + +func (repository *pipeRepository) Insert(entities ...IPipeEntity) { + for _, entity := range entities { + repository.pipes[entity.GetPipe()].Input() <- entity + } +} + +type pipeRepository struct { + baseRepository + pipes map[int]IPipe + dispatcher <-chan time.Time +} + +func newPipeRepository(logger ILogger) IPipeRepository { + repository := &pipeRepository{ + baseRepository: newBaseRepository("pipe", "pipes", nil, logger, true), + pipes: make(map[int]IPipe), + dispatcher: time.NewTicker(AUTO_FLUSH_DURATION).C, + } + + for _, descriptor := range repository.GetPipeDescriptors() { + repository.pipes[descriptor.Id()] = NewPipe(descriptor, repository) + } + + go func() { + for { + select { + case <-repository.dispatcher: + for _, pipe := range repository.pipes { + pipe.Signal() <- SIGNAL_FLUSH + } + } + } + }() + + for _, pipe := range repository.pipes { + go pipe.OpenValve() + } + + return repository +} diff --git a/greataped/components/model/repository/remote_activities_repository.go b/greataped/components/model/repository/remote_activities_repository.go new file mode 100644 index 0000000..8de9598 --- /dev/null +++ b/greataped/components/model/repository/remote_activities_repository.go @@ -0,0 +1,305 @@ +package repository + +import ( + . "github.com/xeronith/diamante/contracts/database" + . "github.com/xeronith/diamante/contracts/logging" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" +) + +type remoteActivitiesRepository struct { + baseRepository +} + +func newRemoteActivitiesRepository(logger ILogger) IRemoteActivitiesRepository { + return &remoteActivitiesRepository{ + baseRepository: newBaseRepository("remote_activity", "remote_activities", RemoteActivityEntityType, logger, false), + } +} + +func (repository *remoteActivitiesRepository) Add(entity IRemoteActivityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `remote_activities` (`id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" + return repository.database.InsertSingle(query, entity.Id(), entity.EntryPoint(), entity.Duration(), entity.Successful(), entity.ErrorMessage(), entity.RemoteAddress(), entity.UserAgent(), entity.EventType(), entity.Timestamp(), editor) +} + +func (repository *remoteActivitiesRepository) AddAtomic(transaction IRepositoryTransaction, entity IRemoteActivityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `remote_activities` (`id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" + return repository.database.InsertSingleAtomic(transaction, query, entity.Id(), entity.EntryPoint(), entity.Duration(), entity.Successful(), entity.ErrorMessage(), entity.RemoteAddress(), entity.UserAgent(), entity.EventType(), entity.Timestamp(), editor) +} + +func (repository *remoteActivitiesRepository) FetchById(id int64) (IRemoteActivityEntity, error) { + if id <= 0 { + return nil, ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "SELECT `id`, `entry_point`, `duration`, `successful` = b'1', `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp` FROM `remote_activities` WHERE `id` = ? AND `status` = 0;" + + var remoteActivityEntity IRemoteActivityEntity + if err := repository.database.QuerySingle(func(cursor ICursor) error { + var ( + id int64 + entryPoint string + duration int64 + successful bool + errorMessage string + remoteAddress string + userAgent string + eventType uint32 + timestamp int64 + ) + + if err := cursor.Scan(&id, &entryPoint, &duration, &successful, &errorMessage, &remoteAddress, &userAgent, &eventType, ×tamp); err != nil { + return err + } + + remoteActivityEntity = NewRemoteActivityEntity(id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp) + return nil + }, query, id); err != nil { + return nil, err + } + + return remoteActivityEntity, nil +} + +func (repository *remoteActivitiesRepository) Update(entity IRemoteActivityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `entry_point` = ?, `duration` = ?, `successful` = ?, `error_message` = ?, `remote_address` = ?, `user_agent` = ?, `event_type` = ?, `timestamp` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, entity.EntryPoint(), entity.Duration(), entity.Successful(), entity.ErrorMessage(), entity.RemoteAddress(), entity.UserAgent(), entity.EventType(), entity.Timestamp(), editor, entity.Id()) +} + +func (repository *remoteActivitiesRepository) UpdateAtomic(transaction IRepositoryTransaction, entity IRemoteActivityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `entry_point` = ?, `duration` = ?, `successful` = ?, `error_message` = ?, `remote_address` = ?, `user_agent` = ?, `event_type` = ?, `timestamp` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, entity.EntryPoint(), entity.Duration(), entity.Successful(), entity.ErrorMessage(), entity.RemoteAddress(), entity.UserAgent(), entity.EventType(), entity.Timestamp(), editor, entity.Id()) +} + +func (repository *remoteActivitiesRepository) Remove(entity IRemoteActivityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingle(query, editor, entity.Id()) +} + +func (repository *remoteActivitiesRepository) RemoveAtomic(transaction IRepositoryTransaction, entity IRemoteActivityEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingleAtomic(transaction, query, editor, entity.Id()) +} + +func (repository *remoteActivitiesRepository) FetchAll() (RemoteActivityEntities, error) { + // language=SQL + query := "SELECT `id`, `entry_point`, `duration`, `successful` = b'1', `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp` FROM `remote_activities` WHERE `id` > 0 AND `status` = 0;" + + var remoteActivityEntities RemoteActivityEntities + if err := repository.database.Query(func(cursor ICursor) error { + var ( + id int64 + entryPoint string + duration int64 + successful bool + errorMessage string + remoteAddress string + userAgent string + eventType uint32 + timestamp int64 + ) + + if err := cursor.Scan(&id, &entryPoint, &duration, &successful, &errorMessage, &remoteAddress, &userAgent, &eventType, ×tamp); err != nil { + return err + } + + remoteActivityEntities = append(remoteActivityEntities, NewRemoteActivityEntity(id, entryPoint, duration, successful, errorMessage, remoteAddress, userAgent, eventType, timestamp)) + return nil + }, query); err != nil { + return nil, err + } + + return remoteActivityEntities, nil +} + +func (repository *remoteActivitiesRepository) UpdateEntryPoint(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `entry_point` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateEntryPointAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `entry_point` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateDuration(id int64, value int64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `duration` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateDurationAtomic(transaction IRepositoryTransaction, id int64, value int64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `duration` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateSuccessful(id int64, value bool, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `successful` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateSuccessfulAtomic(transaction IRepositoryTransaction, id int64, value bool, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `successful` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateErrorMessage(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `error_message` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateErrorMessageAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `error_message` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateRemoteAddress(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `remote_address` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateRemoteAddressAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `remote_address` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateUserAgent(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `user_agent` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateUserAgentAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `user_agent` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateEventType(id int64, value uint32, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `event_type` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateEventTypeAtomic(transaction IRepositoryTransaction, id int64, value uint32, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `event_type` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateTimestamp(id int64, value int64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `timestamp` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *remoteActivitiesRepository) UpdateTimestampAtomic(transaction IRepositoryTransaction, id int64, value int64, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `remote_activities` SET `timestamp` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} diff --git a/greataped/components/model/repository/remote_activities_repository_test.go b/greataped/components/model/repository/remote_activities_repository_test.go new file mode 100644 index 0000000..d14bc58 --- /dev/null +++ b/greataped/components/model/repository/remote_activities_repository_test.go @@ -0,0 +1,631 @@ +package repository_test + +import ( + "testing" + + . "rail.town/infrastructure/components/model/entity" + . "rail.town/infrastructure/components/model/repository" +) + +func TestRemoteActivitiesRepository_Add(test *testing.T) { + type arguments struct { + id int64 + entryPoint string + duration int64 + successful bool + errorMessage string + remoteAddress string + userAgent string + eventType uint32 + timestamp int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + entryPoint: "entry_point", + duration: 0, + successful: true, + errorMessage: "error_message", + remoteAddress: "remote_address", + userAgent: "user_agent", + eventType: 0, + timestamp: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + entryPoint: "entry_point", + duration: 0, + successful: true, + errorMessage: "error_message", + remoteAddress: "remote_address", + userAgent: "user_agent", + eventType: 0, + timestamp: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + entryPoint: "entry_point", + duration: 0, + successful: true, + errorMessage: "error_message", + remoteAddress: "remote_address", + userAgent: "user_agent", + eventType: 0, + timestamp: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewRemoteActivityEntity(testCase.arguments.id, testCase.arguments.entryPoint, testCase.arguments.duration, testCase.arguments.successful, testCase.arguments.errorMessage, testCase.arguments.remoteAddress, testCase.arguments.userAgent, testCase.arguments.eventType, testCase.arguments.timestamp) + if result := RemoteActivities.Add(entity, -1) == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.Add() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestRemoteActivitiesRepository_FetchById(test *testing.T) { + type arguments struct { + id int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity, err := RemoteActivities.FetchById(testCase.arguments.id) + if result := err == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.FetchById() = %v, expected %v", result, testCase.expectation) + } + + _ = entity + }) + } +} + +func TestRemoteActivitiesRepository_Update(test *testing.T) { + type arguments struct { + id int64 + entryPoint string + duration int64 + successful bool + errorMessage string + remoteAddress string + userAgent string + eventType uint32 + timestamp int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + entryPoint: "entry_point", + duration: 0, + successful: true, + errorMessage: "error_message", + remoteAddress: "remote_address", + userAgent: "user_agent", + eventType: 0, + timestamp: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + entryPoint: "entry_point", + duration: 0, + successful: true, + errorMessage: "error_message", + remoteAddress: "remote_address", + userAgent: "user_agent", + eventType: 0, + timestamp: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + entryPoint: "entry_point", + duration: 0, + successful: true, + errorMessage: "error_message", + remoteAddress: "remote_address", + userAgent: "user_agent", + eventType: 0, + timestamp: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewRemoteActivityEntity(testCase.arguments.id, testCase.arguments.entryPoint, testCase.arguments.duration, testCase.arguments.successful, testCase.arguments.errorMessage, testCase.arguments.remoteAddress, testCase.arguments.userAgent, testCase.arguments.eventType, testCase.arguments.timestamp) + if result := RemoteActivities.Update(entity, -1) == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.Update() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestRemoteActivitiesRepository_Remove(test *testing.T) { + type arguments struct { + id int64 + entryPoint string + duration int64 + successful bool + errorMessage string + remoteAddress string + userAgent string + eventType uint32 + timestamp int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewRemoteActivityEntity(testCase.arguments.id, testCase.arguments.entryPoint, testCase.arguments.duration, testCase.arguments.successful, testCase.arguments.errorMessage, testCase.arguments.remoteAddress, testCase.arguments.userAgent, testCase.arguments.eventType, testCase.arguments.timestamp) + if result := RemoteActivities.Remove(entity, -1) == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.Remove() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestRemoteActivitiesRepository_FetchAll(test *testing.T) { + entities, err := RemoteActivities.FetchAll() + if err != nil { + test.Fatal(err) + } + + _ = entities +} + +func TestRemoteActivitiesRepository_UpdateEntryPoint(test *testing.T) { + type arguments struct { + id int64 + entryPoint string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + entryPoint: "entry_point", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + entryPoint: "entry_point", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + entryPoint: "entry_point", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := RemoteActivities.UpdateEntryPoint(testCase.arguments.id, testCase.arguments.entryPoint, -1) == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.UpdateEntryPoint() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestRemoteActivitiesRepository_UpdateDuration(test *testing.T) { + type arguments struct { + id int64 + duration int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + duration: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + duration: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + duration: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := RemoteActivities.UpdateDuration(testCase.arguments.id, testCase.arguments.duration, -1) == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.UpdateDuration() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestRemoteActivitiesRepository_UpdateSuccessful(test *testing.T) { + type arguments struct { + id int64 + successful bool + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + successful: true, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + successful: true, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + successful: true, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := RemoteActivities.UpdateSuccessful(testCase.arguments.id, testCase.arguments.successful, -1) == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.UpdateSuccessful() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestRemoteActivitiesRepository_UpdateErrorMessage(test *testing.T) { + type arguments struct { + id int64 + errorMessage string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + errorMessage: "error_message", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + errorMessage: "error_message", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + errorMessage: "error_message", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := RemoteActivities.UpdateErrorMessage(testCase.arguments.id, testCase.arguments.errorMessage, -1) == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.UpdateErrorMessage() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestRemoteActivitiesRepository_UpdateRemoteAddress(test *testing.T) { + type arguments struct { + id int64 + remoteAddress string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + remoteAddress: "remote_address", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + remoteAddress: "remote_address", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + remoteAddress: "remote_address", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := RemoteActivities.UpdateRemoteAddress(testCase.arguments.id, testCase.arguments.remoteAddress, -1) == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.UpdateRemoteAddress() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestRemoteActivitiesRepository_UpdateUserAgent(test *testing.T) { + type arguments struct { + id int64 + userAgent string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + userAgent: "user_agent", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + userAgent: "user_agent", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + userAgent: "user_agent", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := RemoteActivities.UpdateUserAgent(testCase.arguments.id, testCase.arguments.userAgent, -1) == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.UpdateUserAgent() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestRemoteActivitiesRepository_UpdateEventType(test *testing.T) { + type arguments struct { + id int64 + eventType uint32 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + eventType: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + eventType: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + eventType: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := RemoteActivities.UpdateEventType(testCase.arguments.id, testCase.arguments.eventType, -1) == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.UpdateEventType() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestRemoteActivitiesRepository_UpdateTimestamp(test *testing.T) { + type arguments struct { + id int64 + timestamp int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + timestamp: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + timestamp: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + timestamp: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := RemoteActivities.UpdateTimestamp(testCase.arguments.id, testCase.arguments.timestamp, -1) == nil; result != testCase.expectation { + test.Errorf("RemoteActivities.UpdateTimestamp() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/components/model/repository/scripts/access_control.sql b/greataped/components/model/repository/scripts/access_control.sql new file mode 100644 index 0000000..a462e82 --- /dev/null +++ b/greataped/components/model/repository/scripts/access_control.sql @@ -0,0 +1,42 @@ +USE `###DATABASE###_history`; + +CREATE TABLE `access_controls` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `key` BIGINT UNSIGNED NOT NULL, + `value` BIGINT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +USE `###DATABASE###`; + +CREATE TABLE `access_controls` +( + `id` BIGINT NOT NULL, + `key` BIGINT UNSIGNED NOT NULL, + `value` BIGINT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; diff --git a/greataped/components/model/repository/scripts/access_control_triggers.sql b/greataped/components/model/repository/scripts/access_control_triggers.sql new file mode 100644 index 0000000..da26222 --- /dev/null +++ b/greataped/components/model/repository/scripts/access_control_triggers.sql @@ -0,0 +1,18 @@ + +USE `###DATABASE###`; + +CREATE TRIGGER `access_controls_after_update` +AFTER UPDATE +ON `access_controls` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`access_controls`(`action`, `original_id`, `key`, `value`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`key`, `old`.`value`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; + +CREATE TRIGGER `access_controls_after_delete` +AFTER DELETE +ON `access_controls` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`access_controls`(`action`, `original_id`, `key`, `value`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`key`, `old`.`value`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; diff --git a/greataped/components/model/repository/scripts/category.sql b/greataped/components/model/repository/scripts/category.sql new file mode 100644 index 0000000..e7dfcf4 --- /dev/null +++ b/greataped/components/model/repository/scripts/category.sql @@ -0,0 +1,48 @@ +USE `###DATABASE###_history`; + +CREATE TABLE `categories` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `category_type_id` BIGINT NOT NULL, + `category_id` BIGINT NOT NULL, + `title` VARCHAR(64) NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +USE `###DATABASE###`; + +CREATE TABLE `categories` +( + `id` BIGINT NOT NULL, + `category_type_id` BIGINT NOT NULL, + `category_id` BIGINT NOT NULL, + `title` VARCHAR(64) NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`), + CONSTRAINT `fk_categories_to_category_types` FOREIGN KEY (`category_type_id`) REFERENCES `category_types` (`id`), + CONSTRAINT `fk_categories_to_categories` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; diff --git a/greataped/components/model/repository/scripts/category_triggers.sql b/greataped/components/model/repository/scripts/category_triggers.sql new file mode 100644 index 0000000..1be5e9f --- /dev/null +++ b/greataped/components/model/repository/scripts/category_triggers.sql @@ -0,0 +1,18 @@ + +USE `###DATABASE###`; + +CREATE TRIGGER `categories_after_update` +AFTER UPDATE +ON `categories` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`categories`(`action`, `original_id`, `category_type_id`, `category_id`, `title`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`category_type_id`, `old`.`category_id`, `old`.`title`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; + +CREATE TRIGGER `categories_after_delete` +AFTER DELETE +ON `categories` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`categories`(`action`, `original_id`, `category_type_id`, `category_id`, `title`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`category_type_id`, `old`.`category_id`, `old`.`title`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; diff --git a/greataped/components/model/repository/scripts/category_type.sql b/greataped/components/model/repository/scripts/category_type.sql new file mode 100644 index 0000000..2dc3043 --- /dev/null +++ b/greataped/components/model/repository/scripts/category_type.sql @@ -0,0 +1,40 @@ +USE `###DATABASE###_history`; + +CREATE TABLE `category_types` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +USE `###DATABASE###`; + +CREATE TABLE `category_types` +( + `id` BIGINT NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; diff --git a/greataped/components/model/repository/scripts/category_type_triggers.sql b/greataped/components/model/repository/scripts/category_type_triggers.sql new file mode 100644 index 0000000..8425c57 --- /dev/null +++ b/greataped/components/model/repository/scripts/category_type_triggers.sql @@ -0,0 +1,18 @@ + +USE `###DATABASE###`; + +CREATE TRIGGER `category_types_after_update` +AFTER UPDATE +ON `category_types` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`category_types`(`action`, `original_id`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; + +CREATE TRIGGER `category_types_after_delete` +AFTER DELETE +ON `category_types` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`category_types`(`action`, `original_id`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; diff --git a/greataped/components/model/repository/scripts/document.sql b/greataped/components/model/repository/scripts/document.sql new file mode 100644 index 0000000..8af6d10 --- /dev/null +++ b/greataped/components/model/repository/scripts/document.sql @@ -0,0 +1,40 @@ +USE `###DATABASE###_history`; + +CREATE TABLE `documents` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `content` JSON NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +USE `###DATABASE###`; + +CREATE TABLE `documents` +( + `id` BIGINT NOT NULL, + `content` JSON NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; diff --git a/greataped/components/model/repository/scripts/document_triggers.sql b/greataped/components/model/repository/scripts/document_triggers.sql new file mode 100644 index 0000000..b2e4693 --- /dev/null +++ b/greataped/components/model/repository/scripts/document_triggers.sql @@ -0,0 +1,18 @@ + +USE `###DATABASE###`; + +CREATE TRIGGER `documents_after_update` +AFTER UPDATE +ON `documents` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`documents`(`action`, `original_id`, `content`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`content`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; + +CREATE TRIGGER `documents_after_delete` +AFTER DELETE +ON `documents` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`documents`(`action`, `original_id`, `content`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`content`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; diff --git a/greataped/components/model/repository/scripts/identity.sql b/greataped/components/model/repository/scripts/identity.sql new file mode 100644 index 0000000..da7fe3d --- /dev/null +++ b/greataped/components/model/repository/scripts/identity.sql @@ -0,0 +1,80 @@ +USE `###DATABASE###_history`; + +CREATE TABLE `identities` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `username` VARCHAR(32) NOT NULL, + `phone_number` VARCHAR(12) NOT NULL, + `phone_number_confirmed` BIT(1) NOT NULL, + `first_name` VARCHAR(128) NOT NULL, + `last_name` VARCHAR(128) NOT NULL, + `display_name` VARCHAR(128) NOT NULL, + `email` VARCHAR(128) NOT NULL, + `email_confirmed` BIT(1) NOT NULL, + `avatar` VARCHAR(512) NOT NULL, + `banner` VARCHAR(512) NOT NULL, + `summary` VARCHAR(512) NOT NULL, + `token` VARCHAR(256) NOT NULL, + `multi_factor` BIT(1) NOT NULL, + `hash` VARCHAR(256) NOT NULL, + `salt` VARCHAR(64) NOT NULL, + `public_key` VARCHAR(4096) NOT NULL, + `private_key` VARCHAR(4096) NOT NULL, + `permission` BIGINT UNSIGNED NOT NULL, + `restriction` INT UNSIGNED NOT NULL, + `last_login` BIGINT NOT NULL, + `login_count` INT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +USE `###DATABASE###`; + +CREATE TABLE `identities` +( + `id` BIGINT NOT NULL, + `username` VARCHAR(32) NOT NULL UNIQUE, + `phone_number` VARCHAR(12) NOT NULL, + `phone_number_confirmed` BIT(1) NOT NULL, + `first_name` VARCHAR(128) NOT NULL, + `last_name` VARCHAR(128) NOT NULL, + `display_name` VARCHAR(128) NOT NULL, + `email` VARCHAR(128) NOT NULL UNIQUE, + `email_confirmed` BIT(1) NOT NULL, + `avatar` VARCHAR(512) NOT NULL, + `banner` VARCHAR(512) NOT NULL, + `summary` VARCHAR(512) NOT NULL, + `token` VARCHAR(256) NOT NULL UNIQUE, + `multi_factor` BIT(1) NOT NULL, + `hash` VARCHAR(256) NOT NULL, + `salt` VARCHAR(64) NOT NULL, + `public_key` VARCHAR(4096) NOT NULL, + `private_key` VARCHAR(4096) NOT NULL, + `permission` BIGINT UNSIGNED NOT NULL, + `restriction` INT UNSIGNED NOT NULL, + `last_login` BIGINT NOT NULL, + `login_count` INT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; diff --git a/greataped/components/model/repository/scripts/identity_triggers.sql b/greataped/components/model/repository/scripts/identity_triggers.sql new file mode 100644 index 0000000..0f93c1b --- /dev/null +++ b/greataped/components/model/repository/scripts/identity_triggers.sql @@ -0,0 +1,18 @@ + +USE `###DATABASE###`; + +CREATE TRIGGER `identities_after_update` +AFTER UPDATE +ON `identities` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`identities`(`action`, `original_id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`username`, `old`.`phone_number`, `old`.`phone_number_confirmed`, `old`.`first_name`, `old`.`last_name`, `old`.`display_name`, `old`.`email`, `old`.`email_confirmed`, `old`.`avatar`, `old`.`banner`, `old`.`summary`, `old`.`token`, `old`.`multi_factor`, `old`.`hash`, `old`.`salt`, `old`.`public_key`, `old`.`private_key`, `old`.`permission`, `old`.`restriction`, `old`.`last_login`, `old`.`login_count`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; + +CREATE TRIGGER `identities_after_delete` +AFTER DELETE +ON `identities` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`identities`(`action`, `original_id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`username`, `old`.`phone_number`, `old`.`phone_number_confirmed`, `old`.`first_name`, `old`.`last_name`, `old`.`display_name`, `old`.`email`, `old`.`email_confirmed`, `old`.`avatar`, `old`.`banner`, `old`.`summary`, `old`.`token`, `old`.`multi_factor`, `old`.`hash`, `old`.`salt`, `old`.`public_key`, `old`.`private_key`, `old`.`permission`, `old`.`restriction`, `old`.`last_login`, `old`.`login_count`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; diff --git a/greataped/components/model/repository/scripts/remote_activity.sql b/greataped/components/model/repository/scripts/remote_activity.sql new file mode 100644 index 0000000..9922104 --- /dev/null +++ b/greataped/components/model/repository/scripts/remote_activity.sql @@ -0,0 +1,54 @@ +USE `###DATABASE###_history`; + +CREATE TABLE `remote_activities` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `entry_point` VARCHAR(256) NOT NULL, + `duration` BIGINT NOT NULL, + `successful` BIT(1) NOT NULL, + `error_message` VARCHAR(1024) NOT NULL, + `remote_address` VARCHAR(128) NOT NULL, + `user_agent` VARCHAR(512) NOT NULL, + `event_type` INT UNSIGNED NOT NULL, + `timestamp` BIGINT NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +USE `###DATABASE###`; + +CREATE TABLE `remote_activities` +( + `id` BIGINT NOT NULL, + `entry_point` VARCHAR(256) NOT NULL, + `duration` BIGINT NOT NULL, + `successful` BIT(1) NOT NULL, + `error_message` VARCHAR(1024) NOT NULL, + `remote_address` VARCHAR(128) NOT NULL, + `user_agent` VARCHAR(512) NOT NULL, + `event_type` INT UNSIGNED NOT NULL, + `timestamp` BIGINT NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; diff --git a/greataped/components/model/repository/scripts/remote_activity_triggers.sql b/greataped/components/model/repository/scripts/remote_activity_triggers.sql new file mode 100644 index 0000000..1a1e7b3 --- /dev/null +++ b/greataped/components/model/repository/scripts/remote_activity_triggers.sql @@ -0,0 +1,18 @@ + +USE `###DATABASE###`; + +CREATE TRIGGER `remote_activities_after_update` +AFTER UPDATE +ON `remote_activities` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`remote_activities`(`action`, `original_id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`entry_point`, `old`.`duration`, `old`.`successful`, `old`.`error_message`, `old`.`remote_address`, `old`.`user_agent`, `old`.`event_type`, `old`.`timestamp`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; + +CREATE TRIGGER `remote_activities_after_delete` +AFTER DELETE +ON `remote_activities` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`remote_activities`(`action`, `original_id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`entry_point`, `old`.`duration`, `old`.`successful`, `old`.`error_message`, `old`.`remote_address`, `old`.`user_agent`, `old`.`event_type`, `old`.`timestamp`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; diff --git a/greataped/components/model/repository/scripts/system_schedule.sql b/greataped/components/model/repository/scripts/system_schedule.sql new file mode 100644 index 0000000..50a4e07 --- /dev/null +++ b/greataped/components/model/repository/scripts/system_schedule.sql @@ -0,0 +1,42 @@ +USE `###DATABASE###_history`; + +CREATE TABLE `system_schedules` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `enabled` BIT(1) NOT NULL, + `config` VARCHAR(1024) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +USE `###DATABASE###`; + +CREATE TABLE `system_schedules` +( + `id` BIGINT NOT NULL, + `enabled` BIT(1) NOT NULL, + `config` VARCHAR(1024) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; diff --git a/greataped/components/model/repository/scripts/system_schedule_triggers.sql b/greataped/components/model/repository/scripts/system_schedule_triggers.sql new file mode 100644 index 0000000..b160caa --- /dev/null +++ b/greataped/components/model/repository/scripts/system_schedule_triggers.sql @@ -0,0 +1,18 @@ + +USE `###DATABASE###`; + +CREATE TRIGGER `system_schedules_after_update` +AFTER UPDATE +ON `system_schedules` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`system_schedules`(`action`, `original_id`, `enabled`, `config`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`enabled`, `old`.`config`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; + +CREATE TRIGGER `system_schedules_after_delete` +AFTER DELETE +ON `system_schedules` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`system_schedules`(`action`, `original_id`, `enabled`, `config`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`enabled`, `old`.`config`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; diff --git a/greataped/components/model/repository/scripts/user.sql b/greataped/components/model/repository/scripts/user.sql new file mode 100644 index 0000000..861c694 --- /dev/null +++ b/greataped/components/model/repository/scripts/user.sql @@ -0,0 +1,41 @@ +USE `###DATABASE###_history`; + +CREATE TABLE `users` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `github` VARCHAR(512) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +USE `###DATABASE###`; + +CREATE TABLE `users` +( + `id` BIGINT NOT NULL, + `github` VARCHAR(512) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`), + CONSTRAINT `fk_users_to_identities` FOREIGN KEY (`id`) REFERENCES `identities` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; diff --git a/greataped/components/model/repository/scripts/user_triggers.sql b/greataped/components/model/repository/scripts/user_triggers.sql new file mode 100644 index 0000000..cd5e431 --- /dev/null +++ b/greataped/components/model/repository/scripts/user_triggers.sql @@ -0,0 +1,18 @@ + +USE `###DATABASE###`; + +CREATE TRIGGER `users_after_update` +AFTER UPDATE +ON `users` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`users`(`action`, `original_id`, `github`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`github`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; + +CREATE TRIGGER `users_after_delete` +AFTER DELETE +ON `users` FOR EACH ROW +BEGIN + INSERT INTO `###DATABASE###_history`.`users`(`action`, `original_id`, `github`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`github`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END; diff --git a/greataped/components/model/repository/system_schedules_repository.go b/greataped/components/model/repository/system_schedules_repository.go new file mode 100644 index 0000000..f177b59 --- /dev/null +++ b/greataped/components/model/repository/system_schedules_repository.go @@ -0,0 +1,173 @@ +package repository + +import ( + . "github.com/xeronith/diamante/contracts/database" + . "github.com/xeronith/diamante/contracts/logging" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" +) + +type systemSchedulesRepository struct { + baseRepository +} + +func newSystemSchedulesRepository(logger ILogger) ISystemSchedulesRepository { + return &systemSchedulesRepository{ + baseRepository: newBaseRepository("system_schedule", "system_schedules", SystemScheduleEntityType, logger, false), + } +} + +func (repository *systemSchedulesRepository) Add(entity ISystemScheduleEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `system_schedules` (`id`, `enabled`, `config`, `editor`) VALUES (?, ?, ?, ?);" + return repository.database.InsertSingle(query, entity.Id(), entity.Enabled(), entity.Config(), editor) +} + +func (repository *systemSchedulesRepository) AddAtomic(transaction IRepositoryTransaction, entity ISystemScheduleEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `system_schedules` (`id`, `enabled`, `config`, `editor`) VALUES (?, ?, ?, ?);" + return repository.database.InsertSingleAtomic(transaction, query, entity.Id(), entity.Enabled(), entity.Config(), editor) +} + +func (repository *systemSchedulesRepository) FetchById(id int64) (ISystemScheduleEntity, error) { + if id <= 0 { + return nil, ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "SELECT `id`, `enabled` = b'1', `config` FROM `system_schedules` WHERE `id` = ? AND `status` = 0;" + + var systemScheduleEntity ISystemScheduleEntity + if err := repository.database.QuerySingle(func(cursor ICursor) error { + var ( + id int64 + enabled bool + config string + ) + + if err := cursor.Scan(&id, &enabled, &config); err != nil { + return err + } + + systemScheduleEntity = NewSystemScheduleEntity(id, enabled, config) + return nil + }, query, id); err != nil { + return nil, err + } + + return systemScheduleEntity, nil +} + +func (repository *systemSchedulesRepository) Update(entity ISystemScheduleEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `system_schedules` SET `enabled` = ?, `config` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, entity.Enabled(), entity.Config(), editor, entity.Id()) +} + +func (repository *systemSchedulesRepository) UpdateAtomic(transaction IRepositoryTransaction, entity ISystemScheduleEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `system_schedules` SET `enabled` = ?, `config` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, entity.Enabled(), entity.Config(), editor, entity.Id()) +} + +func (repository *systemSchedulesRepository) Remove(entity ISystemScheduleEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `system_schedules` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingle(query, editor, entity.Id()) +} + +func (repository *systemSchedulesRepository) RemoveAtomic(transaction IRepositoryTransaction, entity ISystemScheduleEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `system_schedules` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingleAtomic(transaction, query, editor, entity.Id()) +} + +func (repository *systemSchedulesRepository) FetchAll() (SystemScheduleEntities, error) { + // language=SQL + query := "SELECT `id`, `enabled` = b'1', `config` FROM `system_schedules` WHERE `id` > 0 AND `status` = 0;" + + var systemScheduleEntities SystemScheduleEntities + if err := repository.database.Query(func(cursor ICursor) error { + var ( + id int64 + enabled bool + config string + ) + + if err := cursor.Scan(&id, &enabled, &config); err != nil { + return err + } + + systemScheduleEntities = append(systemScheduleEntities, NewSystemScheduleEntity(id, enabled, config)) + return nil + }, query); err != nil { + return nil, err + } + + return systemScheduleEntities, nil +} + +func (repository *systemSchedulesRepository) UpdateEnabled(id int64, value bool, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `system_schedules` SET `enabled` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *systemSchedulesRepository) UpdateEnabledAtomic(transaction IRepositoryTransaction, id int64, value bool, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `system_schedules` SET `enabled` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} + +func (repository *systemSchedulesRepository) UpdateConfig(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `system_schedules` SET `config` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *systemSchedulesRepository) UpdateConfigAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `system_schedules` SET `config` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} diff --git a/greataped/components/model/repository/system_schedules_repository_test.go b/greataped/components/model/repository/system_schedules_repository_test.go new file mode 100644 index 0000000..69b604a --- /dev/null +++ b/greataped/components/model/repository/system_schedules_repository_test.go @@ -0,0 +1,301 @@ +package repository_test + +import ( + "testing" + + . "rail.town/infrastructure/components/model/entity" + . "rail.town/infrastructure/components/model/repository" +) + +func TestSystemSchedulesRepository_Add(test *testing.T) { + type arguments struct { + id int64 + enabled bool + config string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + enabled: true, + config: "config", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + enabled: true, + config: "config", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + enabled: true, + config: "config", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewSystemScheduleEntity(testCase.arguments.id, testCase.arguments.enabled, testCase.arguments.config) + if result := SystemSchedules.Add(entity, -1) == nil; result != testCase.expectation { + test.Errorf("SystemSchedules.Add() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestSystemSchedulesRepository_FetchById(test *testing.T) { + type arguments struct { + id int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity, err := SystemSchedules.FetchById(testCase.arguments.id) + if result := err == nil; result != testCase.expectation { + test.Errorf("SystemSchedules.FetchById() = %v, expected %v", result, testCase.expectation) + } + + _ = entity + }) + } +} + +func TestSystemSchedulesRepository_Update(test *testing.T) { + type arguments struct { + id int64 + enabled bool + config string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + enabled: true, + config: "config", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + enabled: true, + config: "config", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + enabled: true, + config: "config", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewSystemScheduleEntity(testCase.arguments.id, testCase.arguments.enabled, testCase.arguments.config) + if result := SystemSchedules.Update(entity, -1) == nil; result != testCase.expectation { + test.Errorf("SystemSchedules.Update() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestSystemSchedulesRepository_Remove(test *testing.T) { + type arguments struct { + id int64 + enabled bool + config string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewSystemScheduleEntity(testCase.arguments.id, testCase.arguments.enabled, testCase.arguments.config) + if result := SystemSchedules.Remove(entity, -1) == nil; result != testCase.expectation { + test.Errorf("SystemSchedules.Remove() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestSystemSchedulesRepository_FetchAll(test *testing.T) { + entities, err := SystemSchedules.FetchAll() + if err != nil { + test.Fatal(err) + } + + _ = entities +} + +func TestSystemSchedulesRepository_UpdateEnabled(test *testing.T) { + type arguments struct { + id int64 + enabled bool + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + enabled: true, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + enabled: true, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + enabled: true, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := SystemSchedules.UpdateEnabled(testCase.arguments.id, testCase.arguments.enabled, -1) == nil; result != testCase.expectation { + test.Errorf("SystemSchedules.UpdateEnabled() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestSystemSchedulesRepository_UpdateConfig(test *testing.T) { + type arguments struct { + id int64 + config string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + config: "config", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + config: "config", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + config: "config", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := SystemSchedules.UpdateConfig(testCase.arguments.id, testCase.arguments.config, -1) == nil; result != testCase.expectation { + test.Errorf("SystemSchedules.UpdateConfig() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/components/model/repository/users_repository.go b/greataped/components/model/repository/users_repository.go new file mode 100644 index 0000000..bdcdcca --- /dev/null +++ b/greataped/components/model/repository/users_repository.go @@ -0,0 +1,151 @@ +package repository + +import ( + . "github.com/xeronith/diamante/contracts/database" + . "github.com/xeronith/diamante/contracts/logging" + . "rail.town/infrastructure/components/constants" + . "rail.town/infrastructure/components/contracts/model" + . "rail.town/infrastructure/components/model/entity" +) + +type usersRepository struct { + baseRepository +} + +func newUsersRepository(logger ILogger) IUsersRepository { + return &usersRepository{ + baseRepository: newBaseRepository("user", "users", UserEntityType, logger, false), + } +} + +func (repository *usersRepository) Add(entity IUserEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `users` (`id`, `github`, `editor`) VALUES (?, ?, ?);" + return repository.database.InsertSingle(query, entity.Id(), entity.Github(), editor) +} + +func (repository *usersRepository) AddAtomic(transaction IRepositoryTransaction, entity IUserEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "INSERT INTO `users` (`id`, `github`, `editor`) VALUES (?, ?, ?);" + return repository.database.InsertSingleAtomic(transaction, query, entity.Id(), entity.Github(), editor) +} + +func (repository *usersRepository) FetchById(id int64) (IUserEntity, error) { + if id <= 0 { + return nil, ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "SELECT `id`, `github` FROM `users` WHERE `id` = ? AND `status` = 0;" + + var userEntity IUserEntity + if err := repository.database.QuerySingle(func(cursor ICursor) error { + var ( + id int64 + github string + ) + + if err := cursor.Scan(&id, &github); err != nil { + return err + } + + userEntity = NewUserEntity(id, github) + return nil + }, query, id); err != nil { + return nil, err + } + + return userEntity, nil +} + +func (repository *usersRepository) Update(entity IUserEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `users` SET `github` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, entity.Github(), editor, entity.Id()) +} + +func (repository *usersRepository) UpdateAtomic(transaction IRepositoryTransaction, entity IUserEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `users` SET `github` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, entity.Github(), editor, entity.Id()) +} + +func (repository *usersRepository) Remove(entity IUserEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `users` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingle(query, editor, entity.Id()) +} + +func (repository *usersRepository) RemoveAtomic(transaction IRepositoryTransaction, entity IUserEntity, editor int64) error { + if entity.Id() <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `users` SET `status` = 1, `editor` = ? WHERE `id` = ?;" + return repository.database.DeleteSingleAtomic(transaction, query, editor, entity.Id()) +} + +func (repository *usersRepository) FetchAll() (UserEntities, error) { + // language=SQL + query := "SELECT `id`, `github` FROM `users` WHERE `id` > 0 AND `status` = 0;" + + var userEntities UserEntities + if err := repository.database.Query(func(cursor ICursor) error { + var ( + id int64 + github string + ) + + if err := cursor.Scan(&id, &github); err != nil { + return err + } + + userEntities = append(userEntities, NewUserEntity(id, github)) + return nil + }, query); err != nil { + return nil, err + } + + return userEntities, nil +} + +func (repository *usersRepository) UpdateGithub(id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `users` SET `github` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingle(query, value, editor, id) +} + +func (repository *usersRepository) UpdateGithubAtomic(transaction IRepositoryTransaction, id int64, value string, editor int64) error { + if id <= 0 { + return ERROR_INVALID_PARAMETERS + } + + // language=SQL + query := "UPDATE `users` SET `github` = ?, `editor` = ? WHERE `id` = ?;" + return repository.database.UpdateSingleAtomic(transaction, query, value, editor, id) +} diff --git a/greataped/components/model/repository/users_repository_test.go b/greataped/components/model/repository/users_repository_test.go new file mode 100644 index 0000000..922dee8 --- /dev/null +++ b/greataped/components/model/repository/users_repository_test.go @@ -0,0 +1,246 @@ +package repository_test + +import ( + "testing" + + . "rail.town/infrastructure/components/model/entity" + . "rail.town/infrastructure/components/model/repository" +) + +func TestUsersRepository_Add(test *testing.T) { + type arguments struct { + id int64 + github string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + github: "github", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + github: "github", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + github: "github", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewUserEntity(testCase.arguments.id, testCase.arguments.github) + if result := Users.Add(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Users.Add() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestUsersRepository_FetchById(test *testing.T) { + type arguments struct { + id int64 + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity, err := Users.FetchById(testCase.arguments.id) + if result := err == nil; result != testCase.expectation { + test.Errorf("Users.FetchById() = %v, expected %v", result, testCase.expectation) + } + + _ = entity + }) + } +} + +func TestUsersRepository_Update(test *testing.T) { + type arguments struct { + id int64 + github string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + github: "github", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + github: "github", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + github: "github", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewUserEntity(testCase.arguments.id, testCase.arguments.github) + if result := Users.Update(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Users.Update() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestUsersRepository_Remove(test *testing.T) { + type arguments struct { + id int64 + github string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + entity := NewUserEntity(testCase.arguments.id, testCase.arguments.github) + if result := Users.Remove(entity, -1) == nil; result != testCase.expectation { + test.Errorf("Users.Remove() = %v, expected %v", result, testCase.expectation) + } + }) + } +} + +func TestUsersRepository_FetchAll(test *testing.T) { + entities, err := Users.FetchAll() + if err != nil { + test.Fatal(err) + } + + _ = entities +} + +func TestUsersRepository_UpdateGithub(test *testing.T) { + type arguments struct { + id int64 + github string + } + + testCases := []struct { + name string + expectation bool + arguments arguments + }{ + { + name: "Case1", + expectation: false, + arguments: arguments{ + id: 0, + github: "github", + }, + }, + { + name: "Case2", + expectation: false, + arguments: arguments{ + id: 0, + github: "github", + }, + }, + { + name: "Case3", + expectation: false, + arguments: arguments{ + id: 0, + github: "github", + }, + }, + } + + for _, testCase := range testCases { + test.Run(testCase.name, func(test *testing.T) { + if result := Users.UpdateGithub(testCase.arguments.id, testCase.arguments.github, -1) == nil; result != testCase.expectation { + test.Errorf("Users.UpdateGithub() = %v, expected %v", result, testCase.expectation) + } + }) + } +} diff --git a/greataped/components/scripts/.gitkeep b/greataped/components/scripts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/greataped/components/scripts/ddl/development.sql b/greataped/components/scripts/ddl/development.sql new file mode 100644 index 0000000..c0682fa --- /dev/null +++ b/greataped/components/scripts/ddl/development.sql @@ -0,0 +1,614 @@ +DROP DATABASE IF EXISTS `sapphire_dev_history`; +CREATE DATABASE `sapphire_dev_history` CHARSET = `utf8mb4` COLLATE = `utf8mb4_unicode_ci`; +USE `sapphire_dev_history`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Documents +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `documents` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `content` JSON NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ SystemSchedules +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `system_schedules` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `enabled` BIT(1) NOT NULL, + `config` VARCHAR(1024) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Identities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `identities` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `username` VARCHAR(32) NOT NULL, + `phone_number` VARCHAR(12) NOT NULL, + `phone_number_confirmed` BIT(1) NOT NULL, + `first_name` VARCHAR(128) NOT NULL, + `last_name` VARCHAR(128) NOT NULL, + `display_name` VARCHAR(128) NOT NULL, + `email` VARCHAR(128) NOT NULL, + `email_confirmed` BIT(1) NOT NULL, + `avatar` VARCHAR(512) NOT NULL, + `banner` VARCHAR(512) NOT NULL, + `summary` VARCHAR(512) NOT NULL, + `token` VARCHAR(256) NOT NULL, + `multi_factor` BIT(1) NOT NULL, + `hash` VARCHAR(256) NOT NULL, + `salt` VARCHAR(64) NOT NULL, + `public_key` VARCHAR(4096) NOT NULL, + `private_key` VARCHAR(4096) NOT NULL, + `permission` BIGINT UNSIGNED NOT NULL, + `restriction` INT UNSIGNED NOT NULL, + `last_login` BIGINT NOT NULL, + `login_count` INT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ AccessControls +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `access_controls` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `key` BIGINT UNSIGNED NOT NULL, + `value` BIGINT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ RemoteActivities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `remote_activities` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `entry_point` VARCHAR(256) NOT NULL, + `duration` BIGINT NOT NULL, + `successful` BIT(1) NOT NULL, + `error_message` VARCHAR(1024) NOT NULL, + `remote_address` VARCHAR(128) NOT NULL, + `user_agent` VARCHAR(512) NOT NULL, + `event_type` INT UNSIGNED NOT NULL, + `timestamp` BIGINT NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ CategoryTypes +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `category_types` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Categories +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `categories` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `category_type_id` BIGINT NOT NULL, + `category_id` BIGINT NOT NULL, + `title` VARCHAR(64) NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Users +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `users` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `github` VARCHAR(512) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ══════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +DROP DATABASE IF EXISTS `sapphire_dev`; +CREATE DATABASE `sapphire_dev` CHARSET = `utf8mb4` COLLATE = `utf8mb4_unicode_ci`; +USE `sapphire_dev`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Documents +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `documents` +( + `id` BIGINT NOT NULL, + `content` JSON NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `documents_after_update` +AFTER UPDATE +ON `documents` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`documents`(`action`, `original_id`, `content`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`content`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `documents_after_delete` +AFTER DELETE +ON `documents` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`documents`(`action`, `original_id`, `content`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`content`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ SystemSchedules +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `system_schedules` +( + `id` BIGINT NOT NULL, + `enabled` BIT(1) NOT NULL, + `config` VARCHAR(1024) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `system_schedules_after_update` +AFTER UPDATE +ON `system_schedules` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`system_schedules`(`action`, `original_id`, `enabled`, `config`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`enabled`, `old`.`config`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `system_schedules_after_delete` +AFTER DELETE +ON `system_schedules` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`system_schedules`(`action`, `original_id`, `enabled`, `config`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`enabled`, `old`.`config`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Identities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `identities` +( + `id` BIGINT NOT NULL, + `username` VARCHAR(32) NOT NULL UNIQUE, + `phone_number` VARCHAR(12) NOT NULL, + `phone_number_confirmed` BIT(1) NOT NULL, + `first_name` VARCHAR(128) NOT NULL, + `last_name` VARCHAR(128) NOT NULL, + `display_name` VARCHAR(128) NOT NULL, + `email` VARCHAR(128) NOT NULL UNIQUE, + `email_confirmed` BIT(1) NOT NULL, + `avatar` VARCHAR(512) NOT NULL, + `banner` VARCHAR(512) NOT NULL, + `summary` VARCHAR(512) NOT NULL, + `token` VARCHAR(256) NOT NULL UNIQUE, + `multi_factor` BIT(1) NOT NULL, + `hash` VARCHAR(256) NOT NULL, + `salt` VARCHAR(64) NOT NULL, + `public_key` VARCHAR(4096) NOT NULL, + `private_key` VARCHAR(4096) NOT NULL, + `permission` BIGINT UNSIGNED NOT NULL, + `restriction` INT UNSIGNED NOT NULL, + `last_login` BIGINT NOT NULL, + `login_count` INT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `identities_after_update` +AFTER UPDATE +ON `identities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`identities`(`action`, `original_id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`username`, `old`.`phone_number`, `old`.`phone_number_confirmed`, `old`.`first_name`, `old`.`last_name`, `old`.`display_name`, `old`.`email`, `old`.`email_confirmed`, `old`.`avatar`, `old`.`banner`, `old`.`summary`, `old`.`token`, `old`.`multi_factor`, `old`.`hash`, `old`.`salt`, `old`.`public_key`, `old`.`private_key`, `old`.`permission`, `old`.`restriction`, `old`.`last_login`, `old`.`login_count`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `identities_after_delete` +AFTER DELETE +ON `identities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`identities`(`action`, `original_id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`username`, `old`.`phone_number`, `old`.`phone_number_confirmed`, `old`.`first_name`, `old`.`last_name`, `old`.`display_name`, `old`.`email`, `old`.`email_confirmed`, `old`.`avatar`, `old`.`banner`, `old`.`summary`, `old`.`token`, `old`.`multi_factor`, `old`.`hash`, `old`.`salt`, `old`.`public_key`, `old`.`private_key`, `old`.`permission`, `old`.`restriction`, `old`.`last_login`, `old`.`login_count`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ AccessControls +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `access_controls` +( + `id` BIGINT NOT NULL, + `key` BIGINT UNSIGNED NOT NULL, + `value` BIGINT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `access_controls_after_update` +AFTER UPDATE +ON `access_controls` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`access_controls`(`action`, `original_id`, `key`, `value`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`key`, `old`.`value`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `access_controls_after_delete` +AFTER DELETE +ON `access_controls` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`access_controls`(`action`, `original_id`, `key`, `value`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`key`, `old`.`value`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ RemoteActivities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `remote_activities` +( + `id` BIGINT NOT NULL, + `entry_point` VARCHAR(256) NOT NULL, + `duration` BIGINT NOT NULL, + `successful` BIT(1) NOT NULL, + `error_message` VARCHAR(1024) NOT NULL, + `remote_address` VARCHAR(128) NOT NULL, + `user_agent` VARCHAR(512) NOT NULL, + `event_type` INT UNSIGNED NOT NULL, + `timestamp` BIGINT NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `remote_activities_after_update` +AFTER UPDATE +ON `remote_activities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`remote_activities`(`action`, `original_id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`entry_point`, `old`.`duration`, `old`.`successful`, `old`.`error_message`, `old`.`remote_address`, `old`.`user_agent`, `old`.`event_type`, `old`.`timestamp`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `remote_activities_after_delete` +AFTER DELETE +ON `remote_activities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`remote_activities`(`action`, `original_id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`entry_point`, `old`.`duration`, `old`.`successful`, `old`.`error_message`, `old`.`remote_address`, `old`.`user_agent`, `old`.`event_type`, `old`.`timestamp`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ CategoryTypes +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `category_types` +( + `id` BIGINT NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `category_types_after_update` +AFTER UPDATE +ON `category_types` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`category_types`(`action`, `original_id`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `category_types_after_delete` +AFTER DELETE +ON `category_types` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`category_types`(`action`, `original_id`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Categories +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `categories` +( + `id` BIGINT NOT NULL, + `category_type_id` BIGINT NOT NULL, + `category_id` BIGINT NOT NULL, + `title` VARCHAR(64) NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`), + CONSTRAINT `fk_categories_to_category_types` FOREIGN KEY (`category_type_id`) REFERENCES `category_types` (`id`), + CONSTRAINT `fk_categories_to_categories` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `categories_after_update` +AFTER UPDATE +ON `categories` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`categories`(`action`, `original_id`, `category_type_id`, `category_id`, `title`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`category_type_id`, `old`.`category_id`, `old`.`title`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `categories_after_delete` +AFTER DELETE +ON `categories` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`categories`(`action`, `original_id`, `category_type_id`, `category_id`, `title`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`category_type_id`, `old`.`category_id`, `old`.`title`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Users +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `users` +( + `id` BIGINT NOT NULL, + `github` VARCHAR(512) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`), + CONSTRAINT `fk_users_to_identities` FOREIGN KEY (`id`) REFERENCES `identities` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `users_after_update` +AFTER UPDATE +ON `users` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`users`(`action`, `original_id`, `github`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`github`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `users_after_delete` +AFTER DELETE +ON `users` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_dev_history`.`users`(`action`, `original_id`, `github`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`github`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Initialization +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +# Identities +INSERT INTO `identities` (`id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`) VALUES (0, 'INVALID', '0', false, '', '', '', 'invalid@localhost', false, '', '', '', '0', b'0', '', '', '', '', 0, 0, 0, 0); +INSERT INTO `identities` (`id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`) VALUES (1, 'root', '1', false, '', '', '', 'root@localhost', false, '', '', '', '1', b'1', '', '', '', '', 0xFFFFFFFF, 0, 0, 0); + +# Users +INSERT INTO `users` (`id`, `github`) VALUES (0, ''); +INSERT INTO `users` (`id`, `github`) VALUES (1, ''); + +# CategoryTypes +INSERT INTO `category_types` (`id`, `description`) VALUES (0, 'INVALID'); + +# Categories +INSERT INTO `categories` (`id`, `category_type_id`, `category_id`, `title`, `description`) VALUES (0, 0, 0, 'INVALID', ''); diff --git a/greataped/components/scripts/ddl/production.sql b/greataped/components/scripts/ddl/production.sql new file mode 100644 index 0000000..ee08bd2 --- /dev/null +++ b/greataped/components/scripts/ddl/production.sql @@ -0,0 +1,614 @@ +DROP DATABASE IF EXISTS `sapphire_history`; +CREATE DATABASE `sapphire_history` CHARSET = `utf8mb4` COLLATE = `utf8mb4_unicode_ci`; +USE `sapphire_history`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Documents +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `documents` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `content` JSON NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ SystemSchedules +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `system_schedules` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `enabled` BIT(1) NOT NULL, + `config` VARCHAR(1024) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Identities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `identities` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `username` VARCHAR(32) NOT NULL, + `phone_number` VARCHAR(12) NOT NULL, + `phone_number_confirmed` BIT(1) NOT NULL, + `first_name` VARCHAR(128) NOT NULL, + `last_name` VARCHAR(128) NOT NULL, + `display_name` VARCHAR(128) NOT NULL, + `email` VARCHAR(128) NOT NULL, + `email_confirmed` BIT(1) NOT NULL, + `avatar` VARCHAR(512) NOT NULL, + `banner` VARCHAR(512) NOT NULL, + `summary` VARCHAR(512) NOT NULL, + `token` VARCHAR(256) NOT NULL, + `multi_factor` BIT(1) NOT NULL, + `hash` VARCHAR(256) NOT NULL, + `salt` VARCHAR(64) NOT NULL, + `public_key` VARCHAR(4096) NOT NULL, + `private_key` VARCHAR(4096) NOT NULL, + `permission` BIGINT UNSIGNED NOT NULL, + `restriction` INT UNSIGNED NOT NULL, + `last_login` BIGINT NOT NULL, + `login_count` INT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ AccessControls +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `access_controls` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `key` BIGINT UNSIGNED NOT NULL, + `value` BIGINT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ RemoteActivities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `remote_activities` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `entry_point` VARCHAR(256) NOT NULL, + `duration` BIGINT NOT NULL, + `successful` BIT(1) NOT NULL, + `error_message` VARCHAR(1024) NOT NULL, + `remote_address` VARCHAR(128) NOT NULL, + `user_agent` VARCHAR(512) NOT NULL, + `event_type` INT UNSIGNED NOT NULL, + `timestamp` BIGINT NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ CategoryTypes +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `category_types` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Categories +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `categories` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `category_type_id` BIGINT NOT NULL, + `category_id` BIGINT NOT NULL, + `title` VARCHAR(64) NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Users +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `users` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `github` VARCHAR(512) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ══════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +DROP DATABASE IF EXISTS `sapphire`; +CREATE DATABASE `sapphire` CHARSET = `utf8mb4` COLLATE = `utf8mb4_unicode_ci`; +USE `sapphire`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Documents +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `documents` +( + `id` BIGINT NOT NULL, + `content` JSON NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `documents_after_update` +AFTER UPDATE +ON `documents` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`documents`(`action`, `original_id`, `content`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`content`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `documents_after_delete` +AFTER DELETE +ON `documents` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`documents`(`action`, `original_id`, `content`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`content`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ SystemSchedules +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `system_schedules` +( + `id` BIGINT NOT NULL, + `enabled` BIT(1) NOT NULL, + `config` VARCHAR(1024) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `system_schedules_after_update` +AFTER UPDATE +ON `system_schedules` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`system_schedules`(`action`, `original_id`, `enabled`, `config`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`enabled`, `old`.`config`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `system_schedules_after_delete` +AFTER DELETE +ON `system_schedules` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`system_schedules`(`action`, `original_id`, `enabled`, `config`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`enabled`, `old`.`config`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Identities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `identities` +( + `id` BIGINT NOT NULL, + `username` VARCHAR(32) NOT NULL UNIQUE, + `phone_number` VARCHAR(12) NOT NULL, + `phone_number_confirmed` BIT(1) NOT NULL, + `first_name` VARCHAR(128) NOT NULL, + `last_name` VARCHAR(128) NOT NULL, + `display_name` VARCHAR(128) NOT NULL, + `email` VARCHAR(128) NOT NULL UNIQUE, + `email_confirmed` BIT(1) NOT NULL, + `avatar` VARCHAR(512) NOT NULL, + `banner` VARCHAR(512) NOT NULL, + `summary` VARCHAR(512) NOT NULL, + `token` VARCHAR(256) NOT NULL UNIQUE, + `multi_factor` BIT(1) NOT NULL, + `hash` VARCHAR(256) NOT NULL, + `salt` VARCHAR(64) NOT NULL, + `public_key` VARCHAR(4096) NOT NULL, + `private_key` VARCHAR(4096) NOT NULL, + `permission` BIGINT UNSIGNED NOT NULL, + `restriction` INT UNSIGNED NOT NULL, + `last_login` BIGINT NOT NULL, + `login_count` INT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `identities_after_update` +AFTER UPDATE +ON `identities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`identities`(`action`, `original_id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`username`, `old`.`phone_number`, `old`.`phone_number_confirmed`, `old`.`first_name`, `old`.`last_name`, `old`.`display_name`, `old`.`email`, `old`.`email_confirmed`, `old`.`avatar`, `old`.`banner`, `old`.`summary`, `old`.`token`, `old`.`multi_factor`, `old`.`hash`, `old`.`salt`, `old`.`public_key`, `old`.`private_key`, `old`.`permission`, `old`.`restriction`, `old`.`last_login`, `old`.`login_count`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `identities_after_delete` +AFTER DELETE +ON `identities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`identities`(`action`, `original_id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`username`, `old`.`phone_number`, `old`.`phone_number_confirmed`, `old`.`first_name`, `old`.`last_name`, `old`.`display_name`, `old`.`email`, `old`.`email_confirmed`, `old`.`avatar`, `old`.`banner`, `old`.`summary`, `old`.`token`, `old`.`multi_factor`, `old`.`hash`, `old`.`salt`, `old`.`public_key`, `old`.`private_key`, `old`.`permission`, `old`.`restriction`, `old`.`last_login`, `old`.`login_count`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ AccessControls +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `access_controls` +( + `id` BIGINT NOT NULL, + `key` BIGINT UNSIGNED NOT NULL, + `value` BIGINT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `access_controls_after_update` +AFTER UPDATE +ON `access_controls` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`access_controls`(`action`, `original_id`, `key`, `value`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`key`, `old`.`value`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `access_controls_after_delete` +AFTER DELETE +ON `access_controls` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`access_controls`(`action`, `original_id`, `key`, `value`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`key`, `old`.`value`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ RemoteActivities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `remote_activities` +( + `id` BIGINT NOT NULL, + `entry_point` VARCHAR(256) NOT NULL, + `duration` BIGINT NOT NULL, + `successful` BIT(1) NOT NULL, + `error_message` VARCHAR(1024) NOT NULL, + `remote_address` VARCHAR(128) NOT NULL, + `user_agent` VARCHAR(512) NOT NULL, + `event_type` INT UNSIGNED NOT NULL, + `timestamp` BIGINT NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `remote_activities_after_update` +AFTER UPDATE +ON `remote_activities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`remote_activities`(`action`, `original_id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`entry_point`, `old`.`duration`, `old`.`successful`, `old`.`error_message`, `old`.`remote_address`, `old`.`user_agent`, `old`.`event_type`, `old`.`timestamp`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `remote_activities_after_delete` +AFTER DELETE +ON `remote_activities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`remote_activities`(`action`, `original_id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`entry_point`, `old`.`duration`, `old`.`successful`, `old`.`error_message`, `old`.`remote_address`, `old`.`user_agent`, `old`.`event_type`, `old`.`timestamp`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ CategoryTypes +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `category_types` +( + `id` BIGINT NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `category_types_after_update` +AFTER UPDATE +ON `category_types` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`category_types`(`action`, `original_id`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `category_types_after_delete` +AFTER DELETE +ON `category_types` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`category_types`(`action`, `original_id`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Categories +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `categories` +( + `id` BIGINT NOT NULL, + `category_type_id` BIGINT NOT NULL, + `category_id` BIGINT NOT NULL, + `title` VARCHAR(64) NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`), + CONSTRAINT `fk_categories_to_category_types` FOREIGN KEY (`category_type_id`) REFERENCES `category_types` (`id`), + CONSTRAINT `fk_categories_to_categories` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `categories_after_update` +AFTER UPDATE +ON `categories` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`categories`(`action`, `original_id`, `category_type_id`, `category_id`, `title`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`category_type_id`, `old`.`category_id`, `old`.`title`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `categories_after_delete` +AFTER DELETE +ON `categories` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`categories`(`action`, `original_id`, `category_type_id`, `category_id`, `title`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`category_type_id`, `old`.`category_id`, `old`.`title`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Users +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `users` +( + `id` BIGINT NOT NULL, + `github` VARCHAR(512) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`), + CONSTRAINT `fk_users_to_identities` FOREIGN KEY (`id`) REFERENCES `identities` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `users_after_update` +AFTER UPDATE +ON `users` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`users`(`action`, `original_id`, `github`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`github`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `users_after_delete` +AFTER DELETE +ON `users` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_history`.`users`(`action`, `original_id`, `github`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`github`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Initialization +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +# Identities +INSERT INTO `identities` (`id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`) VALUES (0, 'INVALID', '0', false, '', '', '', 'invalid@localhost', false, '', '', '', '0', b'0', '', '', '', '', 0, 0, 0, 0); +INSERT INTO `identities` (`id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`) VALUES (1, 'root', '1', false, '', '', '', 'root@localhost', false, '', '', '', '1', b'1', '', '', '', '', 0xFFFFFFFF, 0, 0, 0); + +# Users +INSERT INTO `users` (`id`, `github`) VALUES (0, ''); +INSERT INTO `users` (`id`, `github`) VALUES (1, ''); + +# CategoryTypes +INSERT INTO `category_types` (`id`, `description`) VALUES (0, 'INVALID'); + +# Categories +INSERT INTO `categories` (`id`, `category_type_id`, `category_id`, `title`, `description`) VALUES (0, 0, 0, 'INVALID', ''); diff --git a/greataped/components/scripts/ddl/staging.sql b/greataped/components/scripts/ddl/staging.sql new file mode 100644 index 0000000..0912e1f --- /dev/null +++ b/greataped/components/scripts/ddl/staging.sql @@ -0,0 +1,614 @@ +DROP DATABASE IF EXISTS `sapphire_staging_history`; +CREATE DATABASE `sapphire_staging_history` CHARSET = `utf8mb4` COLLATE = `utf8mb4_unicode_ci`; +USE `sapphire_staging_history`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Documents +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `documents` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `content` JSON NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ SystemSchedules +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `system_schedules` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `enabled` BIT(1) NOT NULL, + `config` VARCHAR(1024) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Identities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `identities` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `username` VARCHAR(32) NOT NULL, + `phone_number` VARCHAR(12) NOT NULL, + `phone_number_confirmed` BIT(1) NOT NULL, + `first_name` VARCHAR(128) NOT NULL, + `last_name` VARCHAR(128) NOT NULL, + `display_name` VARCHAR(128) NOT NULL, + `email` VARCHAR(128) NOT NULL, + `email_confirmed` BIT(1) NOT NULL, + `avatar` VARCHAR(512) NOT NULL, + `banner` VARCHAR(512) NOT NULL, + `summary` VARCHAR(512) NOT NULL, + `token` VARCHAR(256) NOT NULL, + `multi_factor` BIT(1) NOT NULL, + `hash` VARCHAR(256) NOT NULL, + `salt` VARCHAR(64) NOT NULL, + `public_key` VARCHAR(4096) NOT NULL, + `private_key` VARCHAR(4096) NOT NULL, + `permission` BIGINT UNSIGNED NOT NULL, + `restriction` INT UNSIGNED NOT NULL, + `last_login` BIGINT NOT NULL, + `login_count` INT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ AccessControls +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `access_controls` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `key` BIGINT UNSIGNED NOT NULL, + `value` BIGINT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ RemoteActivities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `remote_activities` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `entry_point` VARCHAR(256) NOT NULL, + `duration` BIGINT NOT NULL, + `successful` BIT(1) NOT NULL, + `error_message` VARCHAR(1024) NOT NULL, + `remote_address` VARCHAR(128) NOT NULL, + `user_agent` VARCHAR(512) NOT NULL, + `event_type` INT UNSIGNED NOT NULL, + `timestamp` BIGINT NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ CategoryTypes +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `category_types` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Categories +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `categories` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `category_type_id` BIGINT NOT NULL, + `category_id` BIGINT NOT NULL, + `title` VARCHAR(64) NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Users +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `users` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `github` VARCHAR(512) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ══════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +DROP DATABASE IF EXISTS `sapphire_staging`; +CREATE DATABASE `sapphire_staging` CHARSET = `utf8mb4` COLLATE = `utf8mb4_unicode_ci`; +USE `sapphire_staging`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Documents +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `documents` +( + `id` BIGINT NOT NULL, + `content` JSON NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `documents_after_update` +AFTER UPDATE +ON `documents` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`documents`(`action`, `original_id`, `content`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`content`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `documents_after_delete` +AFTER DELETE +ON `documents` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`documents`(`action`, `original_id`, `content`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`content`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ SystemSchedules +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `system_schedules` +( + `id` BIGINT NOT NULL, + `enabled` BIT(1) NOT NULL, + `config` VARCHAR(1024) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `system_schedules_after_update` +AFTER UPDATE +ON `system_schedules` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`system_schedules`(`action`, `original_id`, `enabled`, `config`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`enabled`, `old`.`config`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `system_schedules_after_delete` +AFTER DELETE +ON `system_schedules` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`system_schedules`(`action`, `original_id`, `enabled`, `config`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`enabled`, `old`.`config`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Identities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `identities` +( + `id` BIGINT NOT NULL, + `username` VARCHAR(32) NOT NULL UNIQUE, + `phone_number` VARCHAR(12) NOT NULL, + `phone_number_confirmed` BIT(1) NOT NULL, + `first_name` VARCHAR(128) NOT NULL, + `last_name` VARCHAR(128) NOT NULL, + `display_name` VARCHAR(128) NOT NULL, + `email` VARCHAR(128) NOT NULL UNIQUE, + `email_confirmed` BIT(1) NOT NULL, + `avatar` VARCHAR(512) NOT NULL, + `banner` VARCHAR(512) NOT NULL, + `summary` VARCHAR(512) NOT NULL, + `token` VARCHAR(256) NOT NULL UNIQUE, + `multi_factor` BIT(1) NOT NULL, + `hash` VARCHAR(256) NOT NULL, + `salt` VARCHAR(64) NOT NULL, + `public_key` VARCHAR(4096) NOT NULL, + `private_key` VARCHAR(4096) NOT NULL, + `permission` BIGINT UNSIGNED NOT NULL, + `restriction` INT UNSIGNED NOT NULL, + `last_login` BIGINT NOT NULL, + `login_count` INT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `identities_after_update` +AFTER UPDATE +ON `identities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`identities`(`action`, `original_id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`username`, `old`.`phone_number`, `old`.`phone_number_confirmed`, `old`.`first_name`, `old`.`last_name`, `old`.`display_name`, `old`.`email`, `old`.`email_confirmed`, `old`.`avatar`, `old`.`banner`, `old`.`summary`, `old`.`token`, `old`.`multi_factor`, `old`.`hash`, `old`.`salt`, `old`.`public_key`, `old`.`private_key`, `old`.`permission`, `old`.`restriction`, `old`.`last_login`, `old`.`login_count`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `identities_after_delete` +AFTER DELETE +ON `identities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`identities`(`action`, `original_id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`username`, `old`.`phone_number`, `old`.`phone_number_confirmed`, `old`.`first_name`, `old`.`last_name`, `old`.`display_name`, `old`.`email`, `old`.`email_confirmed`, `old`.`avatar`, `old`.`banner`, `old`.`summary`, `old`.`token`, `old`.`multi_factor`, `old`.`hash`, `old`.`salt`, `old`.`public_key`, `old`.`private_key`, `old`.`permission`, `old`.`restriction`, `old`.`last_login`, `old`.`login_count`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ AccessControls +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `access_controls` +( + `id` BIGINT NOT NULL, + `key` BIGINT UNSIGNED NOT NULL, + `value` BIGINT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `access_controls_after_update` +AFTER UPDATE +ON `access_controls` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`access_controls`(`action`, `original_id`, `key`, `value`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`key`, `old`.`value`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `access_controls_after_delete` +AFTER DELETE +ON `access_controls` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`access_controls`(`action`, `original_id`, `key`, `value`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`key`, `old`.`value`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ RemoteActivities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `remote_activities` +( + `id` BIGINT NOT NULL, + `entry_point` VARCHAR(256) NOT NULL, + `duration` BIGINT NOT NULL, + `successful` BIT(1) NOT NULL, + `error_message` VARCHAR(1024) NOT NULL, + `remote_address` VARCHAR(128) NOT NULL, + `user_agent` VARCHAR(512) NOT NULL, + `event_type` INT UNSIGNED NOT NULL, + `timestamp` BIGINT NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `remote_activities_after_update` +AFTER UPDATE +ON `remote_activities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`remote_activities`(`action`, `original_id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`entry_point`, `old`.`duration`, `old`.`successful`, `old`.`error_message`, `old`.`remote_address`, `old`.`user_agent`, `old`.`event_type`, `old`.`timestamp`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `remote_activities_after_delete` +AFTER DELETE +ON `remote_activities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`remote_activities`(`action`, `original_id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`entry_point`, `old`.`duration`, `old`.`successful`, `old`.`error_message`, `old`.`remote_address`, `old`.`user_agent`, `old`.`event_type`, `old`.`timestamp`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ CategoryTypes +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `category_types` +( + `id` BIGINT NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `category_types_after_update` +AFTER UPDATE +ON `category_types` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`category_types`(`action`, `original_id`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `category_types_after_delete` +AFTER DELETE +ON `category_types` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`category_types`(`action`, `original_id`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Categories +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `categories` +( + `id` BIGINT NOT NULL, + `category_type_id` BIGINT NOT NULL, + `category_id` BIGINT NOT NULL, + `title` VARCHAR(64) NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`), + CONSTRAINT `fk_categories_to_category_types` FOREIGN KEY (`category_type_id`) REFERENCES `category_types` (`id`), + CONSTRAINT `fk_categories_to_categories` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `categories_after_update` +AFTER UPDATE +ON `categories` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`categories`(`action`, `original_id`, `category_type_id`, `category_id`, `title`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`category_type_id`, `old`.`category_id`, `old`.`title`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `categories_after_delete` +AFTER DELETE +ON `categories` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`categories`(`action`, `original_id`, `category_type_id`, `category_id`, `title`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`category_type_id`, `old`.`category_id`, `old`.`title`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Users +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `users` +( + `id` BIGINT NOT NULL, + `github` VARCHAR(512) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`), + CONSTRAINT `fk_users_to_identities` FOREIGN KEY (`id`) REFERENCES `identities` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `users_after_update` +AFTER UPDATE +ON `users` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`users`(`action`, `original_id`, `github`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`github`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `users_after_delete` +AFTER DELETE +ON `users` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_staging_history`.`users`(`action`, `original_id`, `github`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`github`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Initialization +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +# Identities +INSERT INTO `identities` (`id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`) VALUES (0, 'INVALID', '0', false, '', '', '', 'invalid@localhost', false, '', '', '', '0', b'0', '', '', '', '', 0, 0, 0, 0); +INSERT INTO `identities` (`id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`) VALUES (1, 'root', '1', false, '', '', '', 'root@localhost', false, '', '', '', '1', b'1', '', '', '', '', 0xFFFFFFFF, 0, 0, 0); + +# Users +INSERT INTO `users` (`id`, `github`) VALUES (0, ''); +INSERT INTO `users` (`id`, `github`) VALUES (1, ''); + +# CategoryTypes +INSERT INTO `category_types` (`id`, `description`) VALUES (0, 'INVALID'); + +# Categories +INSERT INTO `categories` (`id`, `category_type_id`, `category_id`, `title`, `description`) VALUES (0, 0, 0, 'INVALID', ''); diff --git a/greataped/components/scripts/ddl/test.sql b/greataped/components/scripts/ddl/test.sql new file mode 100644 index 0000000..94c52f9 --- /dev/null +++ b/greataped/components/scripts/ddl/test.sql @@ -0,0 +1,614 @@ +DROP DATABASE IF EXISTS `sapphire_test_history`; +CREATE DATABASE `sapphire_test_history` CHARSET = `utf8mb4` COLLATE = `utf8mb4_unicode_ci`; +USE `sapphire_test_history`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Documents +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `documents` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `content` JSON NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ SystemSchedules +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `system_schedules` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `enabled` BIT(1) NOT NULL, + `config` VARCHAR(1024) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Identities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `identities` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `username` VARCHAR(32) NOT NULL, + `phone_number` VARCHAR(12) NOT NULL, + `phone_number_confirmed` BIT(1) NOT NULL, + `first_name` VARCHAR(128) NOT NULL, + `last_name` VARCHAR(128) NOT NULL, + `display_name` VARCHAR(128) NOT NULL, + `email` VARCHAR(128) NOT NULL, + `email_confirmed` BIT(1) NOT NULL, + `avatar` VARCHAR(512) NOT NULL, + `banner` VARCHAR(512) NOT NULL, + `summary` VARCHAR(512) NOT NULL, + `token` VARCHAR(256) NOT NULL, + `multi_factor` BIT(1) NOT NULL, + `hash` VARCHAR(256) NOT NULL, + `salt` VARCHAR(64) NOT NULL, + `public_key` VARCHAR(4096) NOT NULL, + `private_key` VARCHAR(4096) NOT NULL, + `permission` BIGINT UNSIGNED NOT NULL, + `restriction` INT UNSIGNED NOT NULL, + `last_login` BIGINT NOT NULL, + `login_count` INT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ AccessControls +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `access_controls` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `key` BIGINT UNSIGNED NOT NULL, + `value` BIGINT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ RemoteActivities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `remote_activities` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `entry_point` VARCHAR(256) NOT NULL, + `duration` BIGINT NOT NULL, + `successful` BIT(1) NOT NULL, + `error_message` VARCHAR(1024) NOT NULL, + `remote_address` VARCHAR(128) NOT NULL, + `user_agent` VARCHAR(512) NOT NULL, + `event_type` INT UNSIGNED NOT NULL, + `timestamp` BIGINT NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ CategoryTypes +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `category_types` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Categories +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `categories` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `category_type_id` BIGINT NOT NULL, + `category_id` BIGINT NOT NULL, + `title` VARCHAR(64) NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Users +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `users` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `action` VARCHAR(16) NOT NULL, + `original_id` BIGINT NOT NULL, + `github` VARCHAR(512) NOT NULL, + `editor` BIGINT NOT NULL, + `status` BIGINT NOT NULL, + `sort_order` FLOAT NOT NULL, + `queued_at` BIGINT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `triggered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +# ══════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +DROP DATABASE IF EXISTS `sapphire_test`; +CREATE DATABASE `sapphire_test` CHARSET = `utf8mb4` COLLATE = `utf8mb4_unicode_ci`; +USE `sapphire_test`; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Documents +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `documents` +( + `id` BIGINT NOT NULL, + `content` JSON NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `documents_after_update` +AFTER UPDATE +ON `documents` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`documents`(`action`, `original_id`, `content`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`content`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `documents_after_delete` +AFTER DELETE +ON `documents` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`documents`(`action`, `original_id`, `content`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`content`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ SystemSchedules +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `system_schedules` +( + `id` BIGINT NOT NULL, + `enabled` BIT(1) NOT NULL, + `config` VARCHAR(1024) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `system_schedules_after_update` +AFTER UPDATE +ON `system_schedules` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`system_schedules`(`action`, `original_id`, `enabled`, `config`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`enabled`, `old`.`config`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `system_schedules_after_delete` +AFTER DELETE +ON `system_schedules` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`system_schedules`(`action`, `original_id`, `enabled`, `config`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`enabled`, `old`.`config`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Identities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `identities` +( + `id` BIGINT NOT NULL, + `username` VARCHAR(32) NOT NULL UNIQUE, + `phone_number` VARCHAR(12) NOT NULL, + `phone_number_confirmed` BIT(1) NOT NULL, + `first_name` VARCHAR(128) NOT NULL, + `last_name` VARCHAR(128) NOT NULL, + `display_name` VARCHAR(128) NOT NULL, + `email` VARCHAR(128) NOT NULL UNIQUE, + `email_confirmed` BIT(1) NOT NULL, + `avatar` VARCHAR(512) NOT NULL, + `banner` VARCHAR(512) NOT NULL, + `summary` VARCHAR(512) NOT NULL, + `token` VARCHAR(256) NOT NULL UNIQUE, + `multi_factor` BIT(1) NOT NULL, + `hash` VARCHAR(256) NOT NULL, + `salt` VARCHAR(64) NOT NULL, + `public_key` VARCHAR(4096) NOT NULL, + `private_key` VARCHAR(4096) NOT NULL, + `permission` BIGINT UNSIGNED NOT NULL, + `restriction` INT UNSIGNED NOT NULL, + `last_login` BIGINT NOT NULL, + `login_count` INT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `identities_after_update` +AFTER UPDATE +ON `identities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`identities`(`action`, `original_id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`username`, `old`.`phone_number`, `old`.`phone_number_confirmed`, `old`.`first_name`, `old`.`last_name`, `old`.`display_name`, `old`.`email`, `old`.`email_confirmed`, `old`.`avatar`, `old`.`banner`, `old`.`summary`, `old`.`token`, `old`.`multi_factor`, `old`.`hash`, `old`.`salt`, `old`.`public_key`, `old`.`private_key`, `old`.`permission`, `old`.`restriction`, `old`.`last_login`, `old`.`login_count`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `identities_after_delete` +AFTER DELETE +ON `identities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`identities`(`action`, `original_id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`username`, `old`.`phone_number`, `old`.`phone_number_confirmed`, `old`.`first_name`, `old`.`last_name`, `old`.`display_name`, `old`.`email`, `old`.`email_confirmed`, `old`.`avatar`, `old`.`banner`, `old`.`summary`, `old`.`token`, `old`.`multi_factor`, `old`.`hash`, `old`.`salt`, `old`.`public_key`, `old`.`private_key`, `old`.`permission`, `old`.`restriction`, `old`.`last_login`, `old`.`login_count`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ AccessControls +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `access_controls` +( + `id` BIGINT NOT NULL, + `key` BIGINT UNSIGNED NOT NULL, + `value` BIGINT UNSIGNED NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `access_controls_after_update` +AFTER UPDATE +ON `access_controls` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`access_controls`(`action`, `original_id`, `key`, `value`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`key`, `old`.`value`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `access_controls_after_delete` +AFTER DELETE +ON `access_controls` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`access_controls`(`action`, `original_id`, `key`, `value`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`key`, `old`.`value`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ RemoteActivities +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `remote_activities` +( + `id` BIGINT NOT NULL, + `entry_point` VARCHAR(256) NOT NULL, + `duration` BIGINT NOT NULL, + `successful` BIT(1) NOT NULL, + `error_message` VARCHAR(1024) NOT NULL, + `remote_address` VARCHAR(128) NOT NULL, + `user_agent` VARCHAR(512) NOT NULL, + `event_type` INT UNSIGNED NOT NULL, + `timestamp` BIGINT NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `remote_activities_after_update` +AFTER UPDATE +ON `remote_activities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`remote_activities`(`action`, `original_id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`entry_point`, `old`.`duration`, `old`.`successful`, `old`.`error_message`, `old`.`remote_address`, `old`.`user_agent`, `old`.`event_type`, `old`.`timestamp`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `remote_activities_after_delete` +AFTER DELETE +ON `remote_activities` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`remote_activities`(`action`, `original_id`, `entry_point`, `duration`, `successful`, `error_message`, `remote_address`, `user_agent`, `event_type`, `timestamp`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`entry_point`, `old`.`duration`, `old`.`successful`, `old`.`error_message`, `old`.`remote_address`, `old`.`user_agent`, `old`.`event_type`, `old`.`timestamp`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ CategoryTypes +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `category_types` +( + `id` BIGINT NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `category_types_after_update` +AFTER UPDATE +ON `category_types` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`category_types`(`action`, `original_id`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `category_types_after_delete` +AFTER DELETE +ON `category_types` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`category_types`(`action`, `original_id`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Categories +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `categories` +( + `id` BIGINT NOT NULL, + `category_type_id` BIGINT NOT NULL, + `category_id` BIGINT NOT NULL, + `title` VARCHAR(64) NOT NULL, + `description` VARCHAR(64) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`), + CONSTRAINT `fk_categories_to_category_types` FOREIGN KEY (`category_type_id`) REFERENCES `category_types` (`id`), + CONSTRAINT `fk_categories_to_categories` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `categories_after_update` +AFTER UPDATE +ON `categories` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`categories`(`action`, `original_id`, `category_type_id`, `category_id`, `title`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`category_type_id`, `old`.`category_id`, `old`.`title`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `categories_after_delete` +AFTER DELETE +ON `categories` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`categories`(`action`, `original_id`, `category_type_id`, `category_id`, `title`, `description`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`category_type_id`, `old`.`category_id`, `old`.`title`, `old`.`description`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Users +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +CREATE TABLE `users` +( + `id` BIGINT NOT NULL, + `github` VARCHAR(512) NOT NULL, + `editor` BIGINT NOT NULL DEFAULT 0, + `status` BIGINT NOT NULL DEFAULT 0, + `sort_order` FLOAT NOT NULL DEFAULT 0, + `queued_at` BIGINT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `payload` JSON NULL, + PRIMARY KEY (`id`), + INDEX (`status`), + CONSTRAINT `fk_users_to_identities` FOREIGN KEY (`id`) REFERENCES `identities` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = `utf8mb4` + COLLATE = `utf8mb4_unicode_ci`; + +DELIMITER $$ + +CREATE TRIGGER `users_after_update` +AFTER UPDATE +ON `users` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`users`(`action`, `original_id`, `github`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('update', `old`.`id`, `old`.`github`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +CREATE TRIGGER `users_after_delete` +AFTER DELETE +ON `users` FOR EACH ROW +BEGIN + INSERT INTO `sapphire_test_history`.`users`(`action`, `original_id`, `github`, `editor`, `status`, `sort_order`, `queued_at`, `created_at`, `updated_at`, `payload`) + VALUES('delete', `old`.`id`, `old`.`github`, `old`.`editor`, `old`.`status`, `old`.`sort_order`, `old`.`queued_at`, `old`.`created_at`, `old`.`updated_at`, `old`.`payload`); +END$$ + +DELIMITER ; + +# ╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════ +# ║ Initialization +# ╚═════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +# Identities +INSERT INTO `identities` (`id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`) VALUES (0, 'INVALID', '0', false, '', '', '', 'invalid@localhost', false, '', '', '', '0', b'0', '', '', '', '', 0, 0, 0, 0); +INSERT INTO `identities` (`id`, `username`, `phone_number`, `phone_number_confirmed`, `first_name`, `last_name`, `display_name`, `email`, `email_confirmed`, `avatar`, `banner`, `summary`, `token`, `multi_factor`, `hash`, `salt`, `public_key`, `private_key`, `permission`, `restriction`, `last_login`, `login_count`) VALUES (1, 'root', '1', false, '', '', '', 'root@localhost', false, '', '', '', '1', b'1', '', '', '', '', 0xFFFFFFFF, 0, 0, 0); + +# Users +INSERT INTO `users` (`id`, `github`) VALUES (0, ''); +INSERT INTO `users` (`id`, `github`) VALUES (1, ''); + +# CategoryTypes +INSERT INTO `category_types` (`id`, `description`) VALUES (0, 'INVALID'); + +# Categories +INSERT INTO `categories` (`id`, `category_type_id`, `category_id`, `title`, `description`) VALUES (0, 0, 0, 'INVALID', ''); diff --git a/greataped/go.mod b/greataped/go.mod new file mode 100644 index 0000000..bc7d928 --- /dev/null +++ b/greataped/go.mod @@ -0,0 +1,41 @@ +module rail.town/infrastructure + +go 1.19 + +require ( + github.com/google/uuid v1.3.0 + github.com/robfig/cron v1.2.0 + github.com/sendgrid/sendgrid-go v3.12.0+incompatible + github.com/xeronith/diamante v1.3.0 + google.golang.org/protobuf v1.28.1 +) + +require ( + github.com/andybalholm/brotli v1.0.4 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect + github.com/go-sql-driver/mysql v1.5.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/gorilla/securecookie v1.1.1 // indirect + github.com/influxdata/influxdb v1.10.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.15.9 // indirect + github.com/labstack/echo v3.3.10+incompatible // indirect + github.com/labstack/gommon v0.4.0 // indirect + github.com/mattn/go-colorable v0.1.11 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nyaruka/phonenumbers v1.1.2 // indirect + github.com/sendgrid/rest v2.6.9+incompatible // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.41.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/crypto v0.1.0 // indirect + golang.org/x/net v0.1.0 // indirect + golang.org/x/sys v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/greataped/go.sum b/greataped/go.sum new file mode 100644 index 0000000..912ec06 --- /dev/null +++ b/greataped/go.sum @@ -0,0 +1,131 @@ +github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 h1:DddqAaWDpywytcG8w/qoQ5sAN8X12d3Z3koB0C3Rxsc= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/influxdata/influxdb v1.10.0 h1:8xDpt8KO3lzrzf/ss+l8r42AGUZvoITu5824berK7SE= +github.com/influxdata/influxdb v1.10.0/go.mod h1:IVPuoA2pOOxau/NguX7ipW0Jp9Bn+dMWlo0+VOscevU= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= +github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= +github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/nyaruka/phonenumbers v1.1.2 h1:MIDljnA08HCUzgNOrkCYja7CJ5U9ylZ+U3Sge8RWW14= +github.com/nyaruka/phonenumbers v1.1.2/go.mod h1:cGaEsOrLjIL0iKGqJR5Rfywy86dSkbApEpXuM9KySNA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= +github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0= +github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= +github.com/sendgrid/sendgrid-go v3.12.0+incompatible h1:/N2vx18Fg1KmQOh6zESc5FJB8pYwt5QFBDflYPh1KVg= +github.com/sendgrid/sendgrid-go v3.12.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY= +github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/xeronith/diamante v1.3.0 h1:dMfWOHiq0AIxuOZCXouulOs11dT7KFjFM+swR6jykbw= +github.com/xeronith/diamante v1.3.0/go.mod h1:BoY6p2S1QFqZznKsCTufTpxIr2ZL17K9Ez2+KPkjPfs= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/greataped/providers/inbound/.gitkeep b/greataped/providers/inbound/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/greataped/providers/inbound/common/.gitkeep b/greataped/providers/inbound/common/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/greataped/providers/outbound/common/.gitkeep b/greataped/providers/outbound/common/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/greataped/providers/outbound/common/messaging/provider.go b/greataped/providers/outbound/common/messaging/provider.go new file mode 100644 index 0000000..05bd700 --- /dev/null +++ b/greataped/providers/outbound/common/messaging/provider.go @@ -0,0 +1,32 @@ +package messaging + +import ( + "fmt" + + . "github.com/xeronith/diamante/contracts/logging" + . "github.com/xeronith/diamante/contracts/messaging" +) + +type provider struct { + name string + logger ILogger + handler IMessagingHandler +} + +func NewProvider(name string, logger ILogger, handler IMessagingHandler) IMessagingProvider { + return &provider{ + name: name, + logger: logger, + handler: handler, + } +} + +func (provider *provider) Send(receiver, message string) error { + if err := provider.handler(receiver, message); err != nil { + provider.logger.Error(fmt.Sprintf("%s: %s", provider.name, err.Error())) + return err + } else { + provider.logger.Debug(fmt.Sprintf("%s: %s %s", provider.name, receiver, message)) + return nil + } +} diff --git a/greataped/providers/outbound/email/handler.go b/greataped/providers/outbound/email/handler.go new file mode 100644 index 0000000..8a2d015 --- /dev/null +++ b/greataped/providers/outbound/email/handler.go @@ -0,0 +1,30 @@ +package email + +import ( + "fmt" + "net/http" + "os" + + "github.com/sendgrid/sendgrid-go" + "github.com/sendgrid/sendgrid-go/helpers/mail" +) + +func handler(receiver, content string) error { + from := mail.NewEmail("Admin", "admin@site") + subject := "Subject" + to := mail.NewEmail(receiver, receiver) + + plainTextContent := content + htmlContent := fmt.Sprintf("

%s

", content) + + message := mail.NewSingleEmail(from, subject, to, plainTextContent, htmlContent) + + client := sendgrid.NewSendClient(os.Getenv("SENDGRID_API_KEY")) + if response, err := client.Send(message); err != nil { + return err + } else if response.StatusCode != http.StatusCreated { + return fmt.Errorf("%d", response.StatusCode) + } + + return nil +} diff --git a/greataped/providers/outbound/email/provider.go b/greataped/providers/outbound/email/provider.go new file mode 100644 index 0000000..d061a97 --- /dev/null +++ b/greataped/providers/outbound/email/provider.go @@ -0,0 +1,25 @@ +package email + +import ( + "github.com/xeronith/diamante/contracts/logging" + "rail.town/infrastructure/providers/outbound/common/messaging" + + "github.com/xeronith/diamante/contracts/email" + . "github.com/xeronith/diamante/contracts/messaging" +) + +const EmailProvider = "EMAIL_PROVIDER" + +type provider struct { + messaging IMessagingProvider +} + +func NewProvider(logger logging.ILogger) email.IEmailProvider { + return &provider{ + messaging: messaging.NewProvider(EmailProvider, logger, handler), + } +} + +func (provider *provider) Send(receiver, message string) error { + return provider.messaging.Send(receiver, message) +} diff --git a/greataped/providers/outbound/email/provider_test.go b/greataped/providers/outbound/email/provider_test.go new file mode 100644 index 0000000..9476b9b --- /dev/null +++ b/greataped/providers/outbound/email/provider_test.go @@ -0,0 +1,17 @@ +package email_test + +import ( + "testing" + + "github.com/xeronith/diamante/logging" + "rail.town/infrastructure/providers/outbound/email" +) + +func Test_Send(t *testing.T) { + logger := logging.NewLogger(false) + provider := email.NewProvider(logger) + + if err := provider.Send("somebody@somewhere.com", "Message content"); err != nil { + t.Fatal(err) + } +} diff --git a/greataped/providers/outbound/sms/handler.go b/greataped/providers/outbound/sms/handler.go new file mode 100644 index 0000000..d3845cc --- /dev/null +++ b/greataped/providers/outbound/sms/handler.go @@ -0,0 +1,7 @@ +package sms + +import "fmt" + +func handler(receiver, message string) error { + return fmt.Errorf("not_implemented %s %s", receiver, message) +} diff --git a/greataped/providers/outbound/sms/provider.go b/greataped/providers/outbound/sms/provider.go new file mode 100644 index 0000000..f0d0bb2 --- /dev/null +++ b/greataped/providers/outbound/sms/provider.go @@ -0,0 +1,25 @@ +package sms + +import ( + "github.com/xeronith/diamante/contracts/logging" + "rail.town/infrastructure/providers/outbound/common/messaging" + + . "github.com/xeronith/diamante/contracts/messaging" + "github.com/xeronith/diamante/contracts/sms" +) + +const SMSProvider = "SMS_PROVIDER" + +type provider struct { + messaging IMessagingProvider +} + +func NewProvider(logger logging.ILogger) sms.ISMSProvider { + return &provider{ + messaging: messaging.NewProvider(SMSProvider, logger, handler), + } +} + +func (provider *provider) Send(receiver, message string) error { + return provider.messaging.Send(receiver, message) +} diff --git a/greataped/providers/outbound/sms/provider_test.go b/greataped/providers/outbound/sms/provider_test.go new file mode 100644 index 0000000..b77384a --- /dev/null +++ b/greataped/providers/outbound/sms/provider_test.go @@ -0,0 +1,17 @@ +package sms_test + +import ( + "testing" + + "github.com/xeronith/diamante/logging" + "rail.town/infrastructure/providers/outbound/sms" +) + +func Test_Send(t *testing.T) { + logger := logging.NewLogger(false) + provider := sms.NewProvider(logger) + + if err := provider.Send("09123456789", "Message content"); err != nil { + t.Fatal(err) + } +} diff --git a/greataped/staticcheck.conf b/greataped/staticcheck.conf new file mode 100644 index 0000000..8b0be8a --- /dev/null +++ b/greataped/staticcheck.conf @@ -0,0 +1 @@ +checks = ["inherit", "-ST1001", "-ST1018", "-ST1012"] \ No newline at end of file