fix folder name for db adapter

This commit is contained in:
2025-03-27 22:09:34 +01:00
parent a56454110a
commit 46077870c3
50 changed files with 2 additions and 1 deletions

View File

@@ -0,0 +1,3 @@
MEMGRAPH_URI="bolt://localhost:7687"
MEMGRAPH_USER="memgraph"
MEMGRAPH_PASSWORD="memgraph"

View File

@@ -0,0 +1,146 @@
version: "2"
linters:
default: none
# This list of linters is not a recommendation (same thing for all this configuration file).
# We intentionally use a limited set of linters.
# See the comment on top of this file.
enable:
- bodyclose
- copyloopvar
- depguard
- dogsled
- dupl
- errcheck
- errorlint
- funlen
- gocheckcompilerdirectives
- gochecknoinits
- goconst
- gocritic
- gocyclo
- godox
- mnd
- goprintffuncname
- gosec
- govet
- intrange
- ineffassign
- lll
- misspell
- nakedret
- noctx
- nolintlint
- revive
- staticcheck
- testifylint
- unconvert
- unparam
- unused
- whitespace
settings:
depguard:
rules:
logger:
deny:
# logging is allowed only by zap.Logger.,
- pkg: "github.com/sirupsen/logrus"
desc: logging is allowed only by zap.Logger.
- pkg: "github.com/pkg/errors"
desc: Should be replaced by standard lib errors package.
- pkg: "github.com/instana/testify"
desc: It's a fork of github.com/stretchr/testify.
dupl:
threshold: 100
funlen:
lines: -1 # the number of lines (code + empty lines) is not a right metric and leads to code without empty line or one-liner.
statements: 60
goconst:
min-len: 2
min-occurrences: 3
gocritic:
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style
disabled-checks:
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- whyNoLint
gocyclo:
min-complexity: 15
godox:
keywords:
- FIXME
mnd:
# don't include the "operation" and "assign"
checks:
- argument
- case
- condition
- return
ignored-numbers:
- '0'
- '1'
- '2'
- '3'
ignored-functions:
- strings.SplitN
govet:
enable-all: true
errorlint:
asserts: false
lll:
line-length: 140
misspell:
locale: US
ignore-rules:
- "importas" # linter name
nolintlint:
allow-unused: false # report any unused nolint directives
require-explanation: true # require an explanation for nolint directives
require-specific: true # require nolint directives to be specific about which linter is being skipped
revive:
rules:
- name: indent-error-flow
- name: unexported-return
disabled: true
- name: unused-parameter
- name: unused-receiver
exclusions:
presets:
- comments
- std-error-handling
- common-false-positives
- legacy
paths:
- internal/api/api.gen.go # generated code
rules:
- path: (.+)_test\.go
linters:
- dupl
- mnd
- lll
formatters:
enable:
- gofmt
- goimports
settings:
gofmt:
rewrite-rules:
- pattern: 'interface{}'
replacement: 'any'
goimports:
local-prefixes:
- github.com/golangci/golangci-lint/v2
exclusions:
paths:
- internal/api/api.gen.go # generated code

View File

@@ -0,0 +1,20 @@
FROM --platform=$BUILDPLATFORM golang:alpine AS build
WORKDIR /app
COPY . .
RUN go get ./...
RUN go generate ./...
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o db-adapter
RUN apk update && apk add ca-certificates && update-ca-certificates
FROM --platform=$TARGETPLATFORM busybox:1.36.1
COPY --from=build /etc/ssl/certs /etc/ssl/certs
COPY --from=build /app/db-adapter /app/
CMD [ "/app/db-adapter" ]

76
apps/db-adapter/go.mod Normal file
View File

@@ -0,0 +1,76 @@
module github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter
go 1.24
require (
github.com/gin-contrib/cors v1.7.4
github.com/gin-contrib/zap v1.1.4
github.com/gin-gonic/gin v1.10.0
github.com/google/uuid v1.6.0
github.com/neo4j/neo4j-go-driver/v5 v5.28.0
github.com/oapi-codegen/runtime v1.1.1
github.com/spf13/viper v1.20.0
github.com/stretchr/testify v1.10.0
go.uber.org/zap v1.27.0
golang.org/x/net v0.37.0
)
require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/bytedance/sonic v1.13.2 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/getkin/kin-openapi v0.127.0 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.25.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/invopop/yaml v0.3.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/sagikazarmark/locafero v0.8.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect
github.com/spf13/afero v1.14.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.15.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/tools v0.31.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen

300
apps/db-adapter/go.sum Normal file
View File

@@ -0,0 +1,300 @@
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/bytedance/sonic v1.12.6 h1:/isNmCUF2x3Sh8RAp/4mh4ZGkcFAX/hLrzrK3AvpRzk=
github.com/bytedance/sonic v1.12.6/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
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/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58=
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w=
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY=
github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
github.com/gin-contrib/cors v1.7.4 h1:/fC6/wk7rCRtqKqki8lLr2Xq+hnV49aXDLIuSek9g4k=
github.com/gin-contrib/cors v1.7.4/go.mod h1:vGc/APSgLMlQfEJV5NAzkrAHb0C8DetL3K6QZuvGii0=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-contrib/zap v1.1.4 h1:xvxTybg6XBdNtcQLH3Tf0lFr4vhDkwzgLLrIGlNTqIo=
github.com/gin-contrib/zap v1.1.4/go.mod h1:7lgEpe91kLbeJkwBTPgtVBy4zMa6oSBEcvj662diqKQ=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
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/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/neo4j/neo4j-go-driver/v5 v5.28.0 h1:chDT68PHNa8JZRmjSkGzAbk1weLWo4rMtDvccvpobg0=
github.com/neo4j/neo4j-go-driver/v5 v5.28.0/go.mod h1:Vff8OwT7QpLm7L2yYr85XNWe9Rbqlbeb9asNXJTHO4k=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 h1:ykgG34472DWey7TSjd8vIfNykXgjOgYJZoQbKfEeY/Q=
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1/go.mod h1:N5+lY1tiTDV3V1BeHtOxeWXHoPVeApvsvjJqegfoaz8=
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
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/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sagikazarmark/locafero v0.8.0 h1:mXaMVw7IqxNBxfv3LdWt9MDmcWDQ1fagDH918lOdVaQ=
github.com/sagikazarmark/locafero v0.8.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/speakeasy-api/openapi-overlay v0.9.0 h1:Wrz6NO02cNlLzx1fB093lBlYxSI54VRhy1aSutx0PQg=
github.com/speakeasy-api/openapi-overlay v0.9.0/go.mod h1:f5FloQrHA7MsxYg9djzMD5h6dxrHjVVByWKh7an8TRc=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY=
github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk=
github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
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.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210112080510-489259a85091/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
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.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
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.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=

View File

@@ -0,0 +1 @@
package api

View File

@@ -0,0 +1,3 @@
package api
//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config oapi-codegen-cfg.yaml ../../../../api/openapi.json

View File

@@ -0,0 +1,6 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/oapi-codegen/oapi-codegen/HEAD/configuration-schema.json
package: api
output: api.gen.go
generate:
models: true
gin-server: true

View File

@@ -0,0 +1,11 @@
package api
import "github.com/gin-gonic/gin"
func (srv *server) CreatePerson(c *gin.Context) {}
func (srv *server) SoftDeletePerson(c *gin.Context, id int, params SoftDeletePersonParams) {}
func (srv *server) UpdatePerson(c *gin.Context, id int, params UpdatePersonParams) {}
func (srv *server) HardDeletePerson(c *gin.Context, id int, params HardDeletePersonParams) {}

View File

@@ -0,0 +1,6 @@
package api
import "github.com/gin-gonic/gin"
func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params CreatePersonAndRelationshipParams) {
}

View File

@@ -0,0 +1,5 @@
package api
import "github.com/gin-gonic/gin"
func (srv *server) GetFamilyTreeById(c *gin.Context, id int) {}

View File

@@ -0,0 +1,9 @@
package api
import "github.com/gin-gonic/gin"
func (srv *server) GetPersonByGoogleId(c *gin.Context, googleId string) {}
func (srv *server) CreatePersonByGoogleIdAndInviteCode(c *gin.Context, googleId string) {}
func (srv *server) CreatePersonByGoogleId(c *gin.Context, googleId string) {}

View File

@@ -0,0 +1,5 @@
package api
import "github.com/gin-gonic/gin"
func (srv *server) GetRecipesByPersonId(c *gin.Context, id int, params GetRecipesByPersonIdParams) {}

View File

@@ -0,0 +1,9 @@
package api
import "github.com/gin-gonic/gin"
func (srv *server) DeleteRecipeRelationship(c *gin.Context, recipeId int, params DeleteRecipeRelationshipParams) {
}
func (srv *server) CreateRecipeRelationship(c *gin.Context, recipeId int, params CreateRecipeRelationshipParams) {
}

View File

@@ -0,0 +1,9 @@
package api
import "github.com/gin-gonic/gin"
func (srv *server) SoftDeleteRecipe(c *gin.Context, id int, params SoftDeleteRecipeParams) {}
func (srv *server) UpdateRecipe(c *gin.Context, id int, params UpdateRecipeParams) {}
func (srv *server) HardDeleteRecipe(c *gin.Context, id int, params HardDeleteRecipeParams) {}

View File

@@ -0,0 +1,7 @@
package api
import "github.com/gin-gonic/gin"
func (srv *server) CreateRelationship(c *gin.Context, params CreateRelationshipParams) {}
func (srv *server) GetRelationship(c *gin.Context, id1 int, id2 int, params GetRelationshipParams) {}

View File

@@ -0,0 +1,34 @@
package api
import (
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/pkg/gin/healthcheck"
"go.uber.org/zap"
)
type server struct {
db neo4j.DriverWithContext
health healthcheck.HealthCheck
logger *zap.Logger
}
func New(logger *zap.Logger, drv neo4j.DriverWithContext, healthcheck healthcheck.HealthCheck) ServerInterface {
if logger == nil {
panic("logger is required")
}
if drv == nil {
panic("neo4j driver is required")
}
if healthcheck == nil {
panic("healthcheck is required")
}
return &server{db: drv, health: healthcheck, logger: logger}
}
func (srv *server) HealthCheck(c *gin.Context) {
srv.health.HealthCheckHandler(c)
}

View File

@@ -0,0 +1,77 @@
package api
import (
"testing"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"go.uber.org/zap"
)
type mockHealthCheck struct {
mock.Mock
}
func (m *mockHealthCheck) HealthCheckHandler(c *gin.Context) {
m.Called(c)
}
func (m *mockHealthCheck) SetStatus(status string) {
m.Called(status)
}
func (m *mockHealthCheck) GetStatus() string {
args := m.Called()
return args.String(0)
}
func TestNewServer(t *testing.T) {
logger := zap.NewNop()
mockDriver, err := neo4j.NewDriverWithContext("bolt+ssc://memgraph:7687", nil)
assert.NoError(t, err)
mockHealth := &mockHealthCheck{}
t.Run("should create a new server instance", func(t *testing.T) {
t.Parallel()
srv := New(logger, mockDriver, mockHealth)
assert.NotNil(t, srv)
})
t.Run("should panic if logger is nil", func(t *testing.T) {
t.Parallel()
assert.Panics(t, func() {
New(nil, mockDriver, mockHealth)
})
})
t.Run("should panic if driver is nil", func(t *testing.T) {
t.Parallel()
assert.Panics(t, func() {
New(logger, nil, mockHealth)
})
})
t.Run("should panic if healthcheck is nil", func(t *testing.T) {
t.Parallel()
assert.Panics(t, func() {
New(logger, mockDriver, nil)
})
})
}
func TestHealthCheck(t *testing.T) {
mockHealth := &mockHealthCheck{}
srv := &server{health: mockHealth}
t.Run("should call HealthCheckHandler", func(t *testing.T) {
mockHealth.On("HealthCheckHandler", mock.Anything).Once()
c, _ := gin.CreateTestContext(nil)
srv.HealthCheck(c)
mockHealth.AssertCalled(t, "HealthCheckHandler", c)
})
}

119
apps/db-adapter/main.go Normal file
View File

@@ -0,0 +1,119 @@
package main
import (
"context"
"errors"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/gin-contrib/cors"
ginzap "github.com/gin-contrib/zap"
"github.com/gin-gonic/gin"
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/api"
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/pkg/gin/healthcheck"
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/pkg/memgraph"
"github.com/spf13/viper"
"go.uber.org/zap"
)
var (
httpPort string
memgraphURI string
memgraphUser string
memgraphPass string
production bool
requestTimeout time.Duration
)
func init() {
viper.AutomaticEnv()
viper.SetDefault("HTTP_PORT", ":80")
viper.SetDefault("MEMGRAPH_URI", "bolt+ssc://memgraph:7687")
viper.SetDefault("MEMGRAPH_USER", "")
viper.SetDefault("MEMGRAPH_PASS", "")
viper.SetDefault("PRODUCTION", false)
viper.SetDefault("REQUEST_TIMEOUT", 20)
httpPort = viper.GetString("HTTP_PORT")
memgraphURI = viper.GetString("MEMGRAPH_URI")
memgraphUser = viper.GetString("MEMGRAPH_USER")
memgraphPass = viper.GetString("MEMGRAPH_PASS")
production = viper.GetBool("PRODUCTION")
requestTimeout = time.Duration(viper.GetInt("REQUEST_TIMEOUT")) * time.Second
}
func main() {
var logger *zap.Logger
var err error
if production {
logger, err = zap.NewProduction()
} else {
logger, _ = zap.NewDevelopment()
}
if err != nil {
panic(err)
}
hc := healthcheck.New()
memgraphDriver := memgraph.InitDatabase(memgraphURI, memgraphUser, memgraphPass)
router := gin.Default()
router.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:5173", "http://localhost"},
AllowCredentials: true,
AllowHeaders: []string{"X-User-ID", "Content-Type"},
MaxAge: 12 * time.Hour,
}))
router.Use(ginzap.Ginzap(logger, time.RFC3339, true))
router.Use(ginzap.RecoveryWithZap(logger, true))
sApi := api.New(logger, memgraphDriver, hc)
api.RegisterHandlersWithOptions(router, sApi, api.GinServerOptions{})
server := &http.Server{
Addr: httpPort,
Handler: router,
ReadTimeout: requestTimeout * time.Second,
WriteTimeout: requestTimeout * time.Second,
}
go func() {
logger.Info("Starting server", zap.String("port", httpPort))
if err := server.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
logger.Fatal(err.Error())
}
}()
// Wait for interrupt signal to gracefully shutdown the server with some time to finish requests.
quit := make(chan os.Signal, 1)
// kill (no param) default send syscall.SIGTERM
// kill -2 is syscall.SIGINT
// kill -9 is syscall.SIGKILL but can't be caught, so don't need to add it
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
<-quit
logger.Info("Shutting down server...")
// The context is used to inform the server it has some seconds to finish
// the request it is currently handling
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout*time.Second)
defer cancel()
go func() {
if err := memgraphDriver.Close(context.WithoutCancel(ctx)); err != nil {
logger.Error("Memgraph driver forced to shutdown", zap.Error(err))
}
}()
if err := server.Shutdown(ctx); err != nil {
logger.Error("Server forced to shutdown", zap.Error(err))
}
logger.Info("Server exited")
}

View File

@@ -0,0 +1,11 @@
package pkg
import (
"os"
)
// Checks if file on path exists or not
func FileExists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}

View File

@@ -0,0 +1,27 @@
package pkg
import (
"os"
"testing"
)
func TestFileExists(t *testing.T) {
// Create a temporary file for testing
file, err := os.CreateTemp("", "testfile")
if err != nil {
t.Fatalf("Failed to create temporary file: %v", err)
}
defer os.Remove(file.Name())
// Test with an existing file
exists := FileExists(file.Name())
if !exists {
t.Errorf("FileExists(%q) = false, want true", file.Name())
}
// Test with a non-existing file
exists = FileExists("non_existing_file.txt")
if exists {
t.Errorf("FileExists(%q) = true, want false", "non_existing_file.txt")
}
}

View File

@@ -0,0 +1,52 @@
package healthcheck
import (
"net/http"
"sync"
"github.com/gin-gonic/gin"
)
type HealthCheck interface {
SetStatus(status string)
GetStatus() string
HealthCheckHandler(c *gin.Context)
}
type healthCheck struct {
status string
sync sync.Mutex
}
func New() HealthCheck {
return &healthCheck{
status: "ok",
}
}
func (hc *healthCheck) SetStatus(status string) {
hc.sync.Lock()
defer hc.sync.Unlock()
hc.status = status
}
func (hc *healthCheck) GetStatus() string {
hc.sync.Lock()
defer hc.sync.Unlock()
return hc.status
}
func (hc *healthCheck) HealthCheckHandler(c *gin.Context) {
switch hc.GetStatus() {
case "nok":
c.JSON(http.StatusServiceUnavailable, gin.H{
"msg": hc.GetStatus(),
})
default:
c.JSON(http.StatusOK, gin.H{
"msg": hc.GetStatus(),
})
}
}

View File

@@ -0,0 +1,134 @@
package healthcheck
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func TestHealthCheck_GetStatus(t *testing.T) {
var hc HealthCheck
type fields struct {
status string
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "Test GetStatus with status ok",
fields: fields{
status: "ok",
},
want: "ok",
},
{
name: "Test GetStatus with status nok",
fields: fields{
status: "nok",
},
want: "nok",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
hc = &healthCheck{
status: tt.fields.status,
}
if got := hc.GetStatus(); got != tt.want {
t.Errorf("HealthCheck.GetStatus() = %v, want %v", got, tt.want)
}
})
}
}
func TestHealthCheck_SetStatus(t *testing.T) {
hc := New()
type args struct {
status string
}
tests := []struct {
name string
args args
want string
}{
{
name: "Test SetStatus with status ok",
args: args{
status: "ok",
},
want: "ok",
},
{
name: "Test SetStatus with status nok",
args: args{
status: "nok",
},
want: "nok",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
hc.SetStatus(tt.args.status)
if got := hc.GetStatus(); got != tt.want {
t.Errorf("HealthCheck.GetStatus() = %v, want %v", got, tt.want)
}
})
}
}
func TestHealthCheck_HealthCheckHandler(t *testing.T) {
t.Parallel()
r := gin.Default()
hc := New()
r.GET("/health", hc.HealthCheckHandler)
type args struct {
status string
}
tests := []struct {
name string
args args
want string
statusCode int
}{
{
name: "Test respond with status ok",
args: args{
status: "ok",
},
want: `{"msg":"ok"}`,
statusCode: http.StatusOK,
},
{
name: "Test respond with status nok",
args: args{
status: "nok",
},
want: `{"msg":"nok"}`,
statusCode: http.StatusServiceUnavailable,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
hc.SetStatus(tt.args.status)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/health", nil)
r.ServeHTTP(w, req)
if got := w.Code; got != tt.statusCode {
t.Errorf("HealthCheck response status code = %v, want %v", got, tt.statusCode)
}
if got := w.Body.String(); got != tt.want {
t.Errorf("HealthCheck response = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -0,0 +1,30 @@
package memgraph
import (
"fmt"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (p *Person) CreatePerson(driver neo4j.DriverWithContext) (*neo4j.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := p.Verify(); err != nil {
return nil, err
}
query := fmt.Sprintf("CREATE (n:Person {%s}) RETURN n;", p.ToString())
result, err := session.Run(ctx, query, nil)
if err != nil {
return nil, err
}
return result.Single(ctx)
}

View File

@@ -0,0 +1,39 @@
package memgraph
import (
"fmt"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (r *Relationship) CreateRelationship(driver neo4j.DriverWithContext) (*neo4j.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := r.Verify(); err != nil {
return nil, err
}
query := fmt.Sprintf(`MATCH (a:Person), (b:Person) WHERE a.id = '%s' AND b.id = '%s'`, r.FirstPersonID, r.SecondPersonID)
if r.Direction == "->" {
query = fmt.Sprintf(`%s CREATE (a)-[r:%s {verified: false}]->(b) RETURN r;`, query, r.Relationship)
} else if r.Direction == "<-" {
query = fmt.Sprintf(`%s CREATE (a)<-[r:%s {verified: false}]-(b) RETURN r;`, query, r.Relationship)
} else {
query = fmt.Sprintf(`%s CREATE (a)<-[r1:%s {verified: True}]-(b) CREATE (a)-[r2:%s {verified: True}]->(b) RETURN r1, r2;`,
query, r.Relationship, r.Relationship)
}
result, err := session.Run(ctx, query, nil)
if err != nil {
return nil, err
}
return result.Single(ctx)
}

View File

@@ -0,0 +1,45 @@
package memgraph
import (
"fmt"
"strings"
"time"
"github.com/google/uuid"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (rp *RelationshipAndPerson) CreateRelationshipAndPerson(driver neo4j.DriverWithContext) (*neo4j.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := rp.Verify(); err != nil {
return nil, err
}
rp.Person.ID = strings.ReplaceAll(uuid.New().String(), "-", "")
query := fmt.Sprintf(`MATCH (a:Person) WHERE a.id = '%s'`, rp.Relationship.FirstPersonID)
query = fmt.Sprintf("%s CREATE (b:Person {%s})", query, rp.Person.ToString())
if rp.Relationship.Direction == "->" {
query = fmt.Sprintf(`%s CREATE (a)-[r:%s {verified: True}]->(b) RETURN r;`, query, rp.Relationship.Relationship)
} else if rp.Relationship.Direction == "<-" {
query = fmt.Sprintf(`%s CREATE (a)<-[r:%s {verified: True}]-(b) RETURN r;`, query, rp.Relationship.Relationship)
} else {
query = fmt.Sprintf(`%s CREATE (a)<-[r1:%s {verified: True}]-(b) CREATE (a)-[r2:%s {verified: True}]->(b) RETURN r1, r2, b;`,
query, rp.Relationship.Relationship, rp.Relationship.Relationship)
}
result, err := session.Run(ctx, query, nil)
if err != nil {
return nil, err
}
return result.Single(ctx)
}

View File

@@ -0,0 +1,66 @@
package memgraph
import (
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
const dbCreateSchemaTimeout = 10 * time.Second
func createIndexes(driver neo4j.DriverWithContext) error {
ctx, cancel := context.WithTimeout(context.Background(), dbCreateSchemaTimeout)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
indexes := []string{
`CREATE INDEX ON :Person(id);`,
`CREATE INDEX ON :Person(last_name);`,
`CREATE INDEX ON :Person(first_name);`,
`CREATE INDEX ON :Person(born);`,
`CREATE INDEX ON :Person(mothers_first_name);`,
`CREATE INDEX ON :Person(mothers_last_name);`,
}
// Run index queries via implicit auto-commit transaction
for _, index := range indexes {
_, err := session.Run(ctx, index, nil)
if err != nil {
return err
}
}
return nil
}
func createConstraints(driver neo4j.DriverWithContext) error {
ctx, cancel := context.WithTimeout(context.Background(), dbCreateSchemaTimeout)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
constraints := []string{
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.id);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.last_name);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.first_name);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.born);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.mothers_first_name);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.mothers_last_name);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT n.id IS UNIQUE;`,
`CREATE CONSTRAINT ON (n:Person) ASSERT n.last_name, n.first_name, n.born, n.mothers_first_name, n.mothers_last_name IS UNIQUE;`,
}
// Run index queries via implicit auto-commit transaction
for _, constraint := range constraints {
_, err := session.Run(ctx, constraint, nil)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,124 @@
package memgraph
import (
"fmt"
"strings"
)
var cypherKeywords = []string{
"CREATE",
"DELETE",
"DETACH",
"DETACH DELETE",
"FOREACH",
"LOAD CSV",
"MERGE",
"MATCH",
"ON",
"OPTIONAL MATCH",
"REMOVE",
"SET",
"START",
"UNION",
"UNWIND",
"WITH",
"RETURN",
"ORDER BY",
"SKIP",
"LIMIT",
"ASC",
"DESC",
"EXISTS",
"CALL",
"USING",
"CONSTRAINT",
"DROP",
"INDEX",
"WHERE",
}
var cypherOperators = []string{
"+",
"-",
"*",
"/",
"%",
"^",
"=",
"<",
">",
"<=",
">=",
"<>",
"AND",
"OR",
"XOR",
"NOT",
"IN",
"STARTS WITH",
"ENDS WITH",
"CONTAINS",
"IS NULL",
"IS NOT NULL",
"IS UNIQUE",
"IS NODE",
"IS RELATIONSHIP",
"IS PROPERTY KEY",
"IS MAP",
"IS LIST",
"IS BOOLEAN",
"IS STRING",
"IS NUMBER",
"IS INTEGER",
"IS FLOAT",
"IS NODE",
"IS RELATIONSHIP",
"IS PATH",
"IS POINT",
"IS DATE",
"IS DURATION",
}
// cypherDelimiters contains the delimiters that need to be escaped in a string to prevent cypher injection keys are the delimiters that need to be escaped and values are the escaped delimiters
var cypherDelimiters = map[string]string{
"'": `\'`,
`"`: `\"`,
`\u0027`: `\\u0027`,
`\u0022`: "\\\\u0022",
"`": "``",
"\\u0060": "\\u0060\\u0060",
}
// VerifyString verifies if a string is valid and does not contain cypher injection
func VerifyString(s string) error {
s = strings.ToUpper(s)
for _, keyword := range cypherKeywords {
if strings.Contains(s, keyword) {
return fmt.Errorf("invalid string: %s contains cypher keyword: %s", s, keyword)
}
}
for _, operator := range cypherOperators {
if strings.Contains(s, operator) {
return fmt.Errorf("invalid string: %s contains cypher operator: %s", s, operator)
}
}
for key := range cypherDelimiters {
if strings.Contains(s, key) {
return fmt.Errorf("invalid string: %s contains cypher delimiter: %s", s, key)
}
}
return nil
}
// EscapeString escapes delimiters in a string to prevent cypher injection
func EscapeString(s string) string {
result := s
for k, v := range cypherDelimiters {
result = strings.ReplaceAll(result, k, v)
}
return result
}

View File

@@ -0,0 +1,27 @@
package memgraph
import (
"fmt"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (p *Person) DeletePerson(driver neo4j.DriverWithContext) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := p.Verify(); err != nil {
return err
}
query := fmt.Sprintf("MATCH (n:Person {id: '%s'}) DELETE n;", p.ID)
_, err := session.Run(ctx, query, nil)
return err
}

View File

@@ -0,0 +1,36 @@
package memgraph
import (
"fmt"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (r *Relationship) DeleteRelationship(driver neo4j.DriverWithContext) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := r.Verify(); err != nil {
return err
}
query := ""
if r.Direction == "->" {
query = fmt.Sprintf(`MATCH (a)-[r:%s]->(b)`, r.Relationship)
} else if r.Direction == "<-" {
query = fmt.Sprintf(`MATCH (a)<-[r:%s]-(b)`, r.Relationship)
} else {
query = fmt.Sprintf(`MATCH (a)-[r:%s]-(b)`, r.Relationship)
}
query = fmt.Sprintf(`%s WHERE a.id = '%s' AND b.id = '%s' DELETE r;`, query, r.FirstPersonID, r.SecondPersonID)
_, err := session.Run(ctx, query, nil)
return err
}

View File

@@ -0,0 +1,32 @@
package memgraph
import (
"context"
"log"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)
func InitDatabase(dbURI, dbUser, dbPassword string) neo4j.DriverWithContext {
driver, err := neo4j.NewDriverWithContext(dbURI, neo4j.BasicAuth(dbUser, dbPassword, ""))
if err != nil {
log.Panicln(err)
}
ctx := context.Background()
err = driver.VerifyConnectivity(ctx)
if err != nil {
log.Panicln(err)
}
if err := createIndexes(driver); err != nil {
log.Panicln(err)
}
if err := createConstraints(driver); err != nil {
log.Panicln(err)
}
return driver
}

View File

@@ -0,0 +1,272 @@
package memgraph
import (
"fmt"
"time"
)
var RelationshipTypes = []string{
"Parent",
"Child",
"Spouse",
"Sibling",
}
type Person struct {
ID string `json:"id"`
Firstname string `json:"first_name"`
Middlename string `json:"middle_name"`
Lastname string `json:"last_name"`
Titles []string `json:"titles"` // e.g. Jr., Sr., III
Suffixes []string `json:"suffixes"` // e.g. Ph.D., M.D.
ExtraNames []string `json:"extra_names"`
Aliases []string `json:"aliases"`
MothersFirstname string `json:"mothers_first_name"`
MothersLastname string `json:"mothers_last_name"`
Born time.Time `json:"born"`
Birthplace string `json:"birthplace"`
Residence string `json:"residence"`
Death time.Time `json:"death"`
Deathplace string `json:"deathplace"`
LifeEvents []map[string]string `json:"life_events"`
Occupations []string `json:"occupation"`
OccupationToDisplay string `json:"occupation_to_display"`
OthersSaid map[string]string `json:"others_said"`
Photos map[string]string `json:"photos"`
ProfilePicture string `json:"profile_picture"`
verified bool
}
func (p *Person) ToString() string {
result := fmt.Sprintf("id: '%s'", p.ID)
if p.Firstname != "" {
result = fmt.Sprintf("%s, first_name: '%s'", result, p.Firstname)
}
if p.Lastname != "" {
result = fmt.Sprintf("%s, last_name: '%s'", result, p.Lastname)
}
if p.Middlename != "" {
result = fmt.Sprintf("%s, middle_name: '%s'", result, p.Middlename)
}
if p.MothersFirstname != "" {
result = fmt.Sprintf("%s, mothers_first_name: '%s'", result, p.MothersFirstname)
}
if p.MothersLastname != "" {
result = fmt.Sprintf("%s, mothers_last_name: '%s'", result, p.MothersLastname)
}
if !p.Born.IsZero() {
result = fmt.Sprintf("%s, born: date({year:%d, month:%d, day:%d})", result, p.Born.Year(), p.Born.Month(), p.Born.Day())
}
if !p.Death.IsZero() {
result = fmt.Sprintf("%s, death: date({year:%d, month:%d, day:%d})", result, p.Death.Year(), p.Death.Month(), p.Death.Day())
}
if p.Birthplace != "" {
result = fmt.Sprintf("%s, birthplace: '%s'", result, p.Birthplace)
}
if p.Residence != "" {
result = fmt.Sprintf("%s, residence: '%s'", result, p.Residence)
}
if p.Deathplace != "" {
result = fmt.Sprintf("%s, deathplace: '%s'", result, p.Deathplace)
}
if p.OccupationToDisplay != "" {
result = fmt.Sprintf("%s, occupation_to_display: '%s'", result, p.OccupationToDisplay)
}
if p.ProfilePicture != "" {
result = fmt.Sprintf("%s, profile_picture: '%s'", result, p.ProfilePicture)
}
if p.Titles != nil && len(p.Titles) > 0 {
result = fmt.Sprintf("%s, titles: [", result)
for _, title := range p.Titles {
result = fmt.Sprintf("%s'%s', ", result, title)
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.Suffixes != nil && len(p.Suffixes) > 0 {
result = fmt.Sprintf("%s, suffixes: [", result)
for _, suffix := range p.Suffixes {
result = fmt.Sprintf("%s'%s', ", result, suffix)
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.ExtraNames != nil && len(p.ExtraNames) > 0 {
result = fmt.Sprintf("%s, extra_names: [", result)
for _, extraName := range p.ExtraNames {
result = fmt.Sprintf("%s'%s', ", result, extraName)
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.Aliases != nil && len(p.Aliases) > 0 {
result = fmt.Sprintf("%s, aliases: [", result)
for _, alias := range p.Aliases {
result = fmt.Sprintf("%s'%s', ", result, alias)
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.LifeEvents != nil && len(p.LifeEvents) > 0 {
result = fmt.Sprintf("%s, life_events: [", result)
for i := 0; i < len(p.LifeEvents); i++ {
date, dok := p.LifeEvents[i]["date"]
event, eok := p.LifeEvents[i]["event"]
if dok && eok {
result = fmt.Sprintf("%s{date: '%s', event: '%s'}, ", result, date, event)
}
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.Occupations != nil && len(p.Occupations) > 0 {
result = fmt.Sprintf("%s, occupations: [", result)
for _, occupation := range p.Occupations {
result = fmt.Sprintf("%s'%s', ", result, occupation)
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.OthersSaid != nil {
result = fmt.Sprintf("%s, others_said: {", result)
for key, value := range p.OthersSaid {
result = fmt.Sprintf("%s%s: '%s', ", result, key, value)
}
result = fmt.Sprintf("%s}", result[:len(result)-2])
}
if p.Photos != nil && len(p.Photos) > 0 {
result = fmt.Sprintf("%s, photos: {", result)
for key, value := range p.Photos {
result = fmt.Sprintf("%s%s: '%s', ", result, key, value)
}
result = fmt.Sprintf("%s}", result[:len(result)-2])
}
return result
}
// Verify checks if the person is valid and does not contain cypher injection it also escapes the delimiters contained in any of the strings
func (p *Person) Verify() error {
if p.verified {
return nil
}
if err := VerifyString(p.ID); err != nil {
return fmt.Errorf("invalid ID type %s", err)
}
p.Firstname = EscapeString(p.Firstname)
p.Middlename = EscapeString(p.Middlename)
p.Lastname = EscapeString(p.Lastname)
p.MothersFirstname = EscapeString(p.MothersFirstname)
p.MothersLastname = EscapeString(p.MothersLastname)
p.Birthplace = EscapeString(p.Birthplace)
p.Residence = EscapeString(p.Residence)
p.Deathplace = EscapeString(p.Deathplace)
p.OccupationToDisplay = EscapeString(p.OccupationToDisplay)
p.ProfilePicture = EscapeString(p.ProfilePicture)
for i, title := range p.Titles {
p.Titles[i] = EscapeString(title)
}
for i, suffix := range p.Suffixes {
p.Suffixes[i] = EscapeString(suffix)
}
for i, extraName := range p.ExtraNames {
p.ExtraNames[i] = EscapeString(extraName)
}
for i, alias := range p.Aliases {
p.Aliases[i] = EscapeString(alias)
}
for i, lifeEvent := range p.LifeEvents {
for key, value := range lifeEvent {
if key != "date" && key != "event" {
return fmt.Errorf("invalid key in life event")
}
p.LifeEvents[i][key] = EscapeString(value)
}
}
for i, occupation := range p.Occupations {
p.Occupations[i] = EscapeString(occupation)
}
for key, value := range p.OthersSaid {
if err := VerifyString(key); err != nil {
return fmt.Errorf("invalid key in others said %s", err)
}
p.OthersSaid[key] = EscapeString(value)
}
for key, value := range p.Photos {
if err := VerifyString(key); err != nil {
return fmt.Errorf("invalid key in photos %s", err)
}
p.Photos[key] = EscapeString(value)
}
p.verified = true
return nil
}
type Relationship struct {
FirstPersonID string `json:"first_person_id"`
SecondPersonID string `json:"second_person_id"`
Relationship string `json:"relationship"`
Direction string `json:"direction"`
}
// Verify checks if the relationship is valid and does not contain cypher injection
func (r *Relationship) Verify() error {
if r.Direction != "->" && r.Direction != "<-" && r.Direction != "-" {
return fmt.Errorf("invalid direction for relationship")
}
// Check if the relationship is in the list of valid relationships
found := false
for _, relationship := range RelationshipTypes {
if r.Relationship == relationship {
found = true
break
}
}
if !found {
return fmt.Errorf("invalid relationship type")
}
if err := VerifyString(r.FirstPersonID); err != nil {
return fmt.Errorf("invalid FirstPersonID type %s", err)
}
if err := VerifyString(r.SecondPersonID); err != nil {
return fmt.Errorf("invalid SecondPersonID type %s", err)
}
return nil
}
type RelationshipAndPerson struct {
Relationship Relationship `json:"relationship"`
Person Person `json:"person"`
}
func (r *RelationshipAndPerson) Verify() error {
if err := r.Relationship.Verify(); err != nil {
return err
}
if err := r.Person.Verify(); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,77 @@
package memgraph
import (
"testing"
"time"
)
func TestPerson_ToString(t *testing.T) {
tests := []struct {
name string
p *Person
want string
}{
{
name: "Test with nil values",
p: &Person{
ID: "1",
Firstname: "John",
Lastname: "Doe",
MothersFirstname: "Jane",
MothersLastname: "Doe",
Born: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Birthplace: "New York",
Residence: "New York",
Death: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Deathplace: "New York",
},
want: "ID: '1', Firstname: 'John', Lastname: 'Doe', MothersFirstname: 'Jane', MothersLastname: 'Doe', Born: date({year:2021, month:1, day:1}), Death: date({year:2021, month:1, day:1}), Birthplace: 'New York', Residence: 'New York', Deathplace: 'New York', OccupationToDisplay: '', ProfilePicture: ''",
}, {
name: "Test with All values",
p: &Person{
ID: "1",
Firstname: "John",
Lastname: "Doe",
MothersFirstname: "Jane",
MothersLastname: "Doe",
Born: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Birthplace: "New York",
Residence: "New York",
Death: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Deathplace: "New York",
LifeEvents: []map[string]string{
{
"date": "2021-01-01",
"event": "Event 1",
},
{
"date": "2021-01-02",
"event": "Event 2",
},
},
Occupations: []string{
"Welder",
"Plumber",
},
OccupationToDisplay: "Welder",
OthersSaid: map[string]string{
"Beni": "He is a good person",
"Jani": "He is a bad person",
},
Photos: map[string]string{
"Profile": "profile.jpg",
"Family": "family.jpg",
},
ProfilePicture: "profile.jpg",
},
want: "ID: '1', Firstname: 'John', Lastname: 'Doe', MothersFirstname: 'Jane', MothersLastname: 'Doe', Born: date({year:2021, month:1, day:1}), Death: date({year:2021, month:1, day:1}), Birthplace: 'New York', Residence: 'New York', Deathplace: 'New York', OccupationToDisplay: 'Welder', ProfilePicture: 'profile.jpg', LifeEvents: [{date: '2021-01-01', event: 'Event 1'}, {date: '2021-01-02', event: 'Event 2'}], Occupations: ['Welder', 'Plumber'], OthersSaid: {Beni: 'He is a good person', Jani: 'He is a bad person'}, Photos: {Profile: 'profile.jpg', Family: 'family.jpg'}",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.p.ToString(); got != tt.want {
t.Errorf("Person.ToString() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -0,0 +1,4 @@
MATCH (a:Person), (b:Person)
WHERE id(a) = $id1 AND id(b) = $id2
CREATE (a)-[r:Relationship $Relationship]->(b)
RETURN r AS relationship

View File

@@ -0,0 +1 @@
CREATE (p:Person $Person) RETURN p as person

View File

@@ -0,0 +1,4 @@
MATCH (a:Person), (b:Person)
WHERE id(a) = $id1 AND id(b) = $id2
CREATE (a)-[r:Relationship $Relationship]-(b)
RETURN r as relationship

View File

@@ -0,0 +1,5 @@
MATCH (a:Person), (b:Person)
WHERE id(a) = $id1 AND id(b) = $id2
CREATE (a)-[r1:Relationship $Relationship1]->(b)
CREATE (b)-[r2:Relationship $Relationship2]->(a)
RETURN r1 as relationship1, r2 as relationship2

View File

@@ -0,0 +1,8 @@
MATCH (n:Person)-[p:Parent*1..]->(family:Person)
WHERE id(n) = $id
OPTIONAL MATCH (family)-[c:Child]->(children:Person)
WITH family, p, children, c, n
OPTIONAL MATCH (children)<-[p2:Parent]-(OtherParents:Person)
WITH family, p, children, c, OtherParents, p2, n
OPTIONAL MATCH (family)-[s:Spouse]-(spouse:Person)
RETURN family, p, children, c, OtherParents, p2, spouse, s, n;

View File

@@ -0,0 +1 @@
MATCH (n:Person) WHERE n.id = $id RETURN n AS people

View File

@@ -0,0 +1 @@
MATCH (n:Person) WHERE n.google_id = $google_id RETURN n as person

View File

@@ -0,0 +1 @@
MATCH (n:Person) WHERE id(n) = $id RETURN n as person

View File

@@ -0,0 +1,3 @@
MATCH (n)-[r]-(o)
WHERE id(n) = $id1 AND id(o) = $id2
RETURN r as relationship

View File

@@ -0,0 +1,3 @@
MATCH (n:DeletedPerson)
WHERE id(n) = $id
DETACH DELETE n;

View File

@@ -0,0 +1,14 @@
CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.last_name);
CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.first_name);
CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.born);
CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.mothers_first_name);
CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.mothers_last_name);
CREATE CONSTRAINT ON (n:Person) ASSERT n.google_id IS UNIQUE;
CREATE CONSTRAINT ON (n:Person) ASSERT n.last_name, n.first_name, n.born, n.mothers_first_name, n.mothers_last_name IS UNIQUE;
CREATE INDEX ON :Person(google_id);
CREATE INDEX ON :Person(last_name);
CREATE INDEX ON :Person(first_name);
CREATE INDEX ON :Person(born);
CREATE INDEX ON :Person(mothers_first_name);
CREATE INDEX ON :Person(mothers_last_name);

View File

@@ -0,0 +1,4 @@
MATCH (n:Person {id: $id})
SET n:DeletedPerson
REMOVE n:Person
RETURN labels(n) AS labels, n AS person

View File

@@ -0,0 +1,4 @@
MATCH (n:Person)
WHERE id(n) = $id
SET n += $props
RETURN n AS person

View File

@@ -0,0 +1,30 @@
package memgraph
import (
"fmt"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (p *Person) UpdatePerson(driver neo4j.DriverWithContext) (*neo4j.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := p.Verify(); err != nil {
return nil, err
}
query := fmt.Sprintf("MATCH (n:Person {id: '%s'}) SET n += {%s} RETURN n;", p.ID, p.ToString())
result, err := session.Run(ctx, query, nil)
if err != nil {
return nil, err
}
return result.Single(ctx)
}

View File

@@ -0,0 +1,39 @@
package memgraph
import (
"fmt"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (r *Relationship) VerifyRelationship(driver neo4j.DriverWithContext) (*neo4j.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := r.Verify(); err != nil {
return nil, err
}
query := ""
if r.Direction == "->" {
query = fmt.Sprintf(`MATCH (a)-[r:%s]->(b)`, r.Relationship)
} else if r.Direction == "<-" {
query = fmt.Sprintf(`MATCH (a)<-[r:%s]-(b)`, r.Relationship)
} else {
query = fmt.Sprintf(`MATCH (a)-[r:%s]-(b)`, r.Relationship)
}
query = fmt.Sprintf(`%s WHERE a.id = %s AND b.id = %s set r.verified = true return r;`, query, r.FirstPersonID, r.SecondPersonID)
result, err := session.Run(ctx, query, nil)
if err != nil {
return nil, err
}
return result.Single(ctx)
}

View File

@@ -0,0 +1,40 @@
package pkg
import (
"errors"
"log"
"net/http"
"time"
)
func SetupHttpsServer(router http.Handler, cert, key, httpsPort, httpPort string, requestTimeout time.Duration) (server *http.Server) {
if FileExists(cert) && FileExists(key) {
server = &http.Server{
Addr: httpsPort,
Handler: router,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
go func() {
log.Println("Server starts at port ", httpsPort)
if err := server.ListenAndServeTLS(cert, key); err != nil && errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}()
} else {
server = &http.Server{
Addr: httpPort,
Handler: router,
ReadTimeout: requestTimeout * time.Second,
WriteTimeout: requestTimeout * time.Second,
}
go func() {
log.Println("Server starts at port ", httpPort)
if err := server.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}()
}
return server
}