diff --git a/.gitignore b/.gitignore index 82eca336e352c9026decda294ff678968050edfc..2a0148e22111eff3ddd5f76ad5d7ba04082cd6be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /target/ !.mvn/wrapper/maven-wrapper.jar +*.log ### STS ### .apt_generated diff --git a/Dockerfile b/Dockerfile index 4afd4e6e8e144946f5e2a5f68bd9f3c9a5d93df8..d58e26b942dd6255d2f4a45a9f9fa244d5b5aefe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,15 @@ FROM openjdk:10.0.2-13-slim -# Define working directory and create it +# Define working directory. ENV WORKING_DIR /opt/nynja -RUN mkdir -p "$WORKING_DIR" +# Install curl for use with Kubernetes readiness probe. +RUN mkdir -p "$WORKING_DIR" \ + && apt-get update \ + && apt-get install -y curl \ + && rm -rf /var/lib/apt/lists/* WORKDIR $WORKING_DIR -# Default configuration properties - HTTP server port, gRPC server port, Cassandra - host, port and database -ENV HTTP_SERVER_PORT=8080 -ENV GRPC_SERVER_PORT=6565 -ENV CASSANDRA_CONTACT_POINTS=localhost -ENV CASSANDRA_PORT=9042 -ENV CASSANDRA_KEYSPACE=account - -# Note: In order to be able to connect and persist accounts in Cassandra DB schema and tables need to be created first. Please refer to the src/main/resources/db/account-service.cql - -# Expose Tomcat and gRPC server ports -EXPOSE $HTTP_SERVER_PORT -EXPOSE $GRPC_SERVER_PORT - # Copy the .jar file into the Docker image COPY ./target/*.jar $WORKING_DIR/account-service.jar -CMD ["java", "-jar", "$WORKING_DIR/account-service.jar", "--spring.profiles.active=production", "-XX:+UseContainerSupport", "-Djava.awt.headless=true", "-server", "-Xms128m", "-Xmx512m"] +CMD ["java", "-jar", "account-service.jar", "--spring.profiles.active=production"] diff --git a/Jenkinsfile b/Jenkinsfile index b1a9335e55765f48e65265c00a003501dc068f20..7d491ce4542e23b29aa346d3116cce75aa25c313 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,83 +1,165 @@ -#!/usr/bin/env groovy - -@Library('nynja-common') _ - -pipeline { - environment { - SLACK_CHANNEL = "#nynja-devops-feed" - // Application namespace (k8s namespace, docker repository, ...) - NAMESPACE = "blueprint" - // Application name - APP_NAME = "account-service" - IMAGE_NAME = "eu.gcr.io/nynja-ci-201610/${NAMESPACE}/${APP_NAME}" - IMAGE_TAG = "${BRANCH_NAME == 'master' ? 'latest' : 'latest-' + BRANCH_NAME}" - IMAGE_BUILD_TAG = "$BRANCH_NAME-$BUILD_NUMBER" - // The branch to be deployed in the dev cluster, following gitflow, it should normally be "develop" or "dev" - DEV_BRANCH = "master" - } - agent { - kubernetes(builders.simple("jdk", "openjdk:11-jdk")) - } - options { - skipDefaultCheckout() - buildDiscarder(logRotator(numToKeepStr: '15')) - } - stages { - stage('Checkout') { - steps { - container('jdk') { - script { - def vars = checkout scm - vars.each { k,v -> env.setProperty(k, v) } - } - - slackSend channel: SLACK_CHANNEL, message: slackStartMsg() - slackSend channel: SLACK_CHANNEL, message: "", attachments: slackBuildInfo() - } - } - } - stage('Build') { - steps { - container('jdk') { - // Maven must be available in the path. - // Tests must be enabled as soon as they pass locally. - mvn clean install - } - } - } - stage('Build & Publish Docker Image') { - when { - branch env.DEV_BRANCH - } - steps { - // The container used to build & publish the docker image must have the docker binary - // either installed, either mounted, like in this blueprint - container('jdk') { - dockerBuildAndPushToRegistry "${NAMESPACE}/${APP_NAME}", [IMAGE_TAG,IMAGE_BUILD_TAG] - } - } - } - stage('Update Kubernetes deployment') { - when { - branch env.DEV_BRANCH - } - steps { - // To deploy, the configuration from the ./k8s folder will be commit to a deployment repository - // environment variables are being substituted in the process. - // The namespace correspond to the target k8s namespace. - // Git must be available in the container used, as configuration will be pushed. - container('jdk') { - deployToDevelopment(NAMESPACE) - } - } - } - } - post { - success { - slackSend channel: SLACK_CHANNEL, message: slackEndMsg(), color: 'good' - } - failure { - slackSend channel: SLACK_CHANNEL, message: slackEndMsg(), color: 'danger' - } - } -} \ No newline at end of file +#!/usr/bin/env groovy + +@Library('nynja-common') _ + +pipeline { + environment { + SLACK_CHANNEL = "#nynja-devops-feed" + NAMESPACE = "account" + APP_NAME = "account-service" + IMAGE_NAME = "eu.gcr.io/nynja-ci-201610/${NAMESPACE}/${APP_NAME}" + IMAGE_BUILD_TAG = "$BRANCH_NAME-$BUILD_NUMBER" + HELM_CHART_NAME = "account-service" + DEV_BRANCH = "dev" + } + agent { + kubernetes(builders.multi([ + "mvn":"maven:3-jdk-10", + "helm":"lachlanevenson/k8s-helm:v2.9.1" + ])) + } + options { + skipDefaultCheckout() + buildDiscarder(logRotator(numToKeepStr: '15')) + } + stages { + stage('Checkout') { + steps { + container('mvn') { + script { + def vars = checkout scm + vars.each { k,v -> env.setProperty(k, v) } + } + slackSend channel: SLACK_CHANNEL, message: slackStartMsg() + slackSend channel: SLACK_CHANNEL, message: "", attachments: slackBuildInfo() + } + } + } + /* + stage('Build PR') { + when { + branch 'PR-*' + } + stages { + stage('Build') { + steps { + echo 'build & test' + dockerBuildAndPushToRegistry "${NAMESPACE}/${APP_NAME}", [IMAGE_BUILD_TAG] + } + } + stage('Deploy preview') { + steps { + echo 'build & test' + } + } + } + } + */ + stage('Build Dev') { + when { + branch env.DEV_BRANCH + } + stages { + stage('Build') { + steps { + container('mvn') { + withCredentials([file(credentialsId: 'mavenSettings.xml', variable: 'FILE')]) { + sh 'mvn --settings $FILE clean install -Dmaven.test.skip=true' + } + dockerBuildAndPushToRegistry "${NAMESPACE}/${APP_NAME}", [IMAGE_BUILD_TAG] + } + } + } + stage("Helm chart") { + steps { + container('helm') { + helmBuildAndPushToRegistry HELM_CHART_NAME + } + } + } + stage('Deploy preview') { + steps { + deployHelmTo "dev", NAMESPACE + } + } + } + post { + success { + container('mvn') { slackSend channel: SLACK_CHANNEL, message: slackEndMsg(), color: 'good' } + } + failure { + container('mvn') { slackSend channel: SLACK_CHANNEL, message: slackEndMsg(), color: 'danger' } + } + } + } + stage('Build Release') { + when { + branch 'master' + } + stages { + stage("Build") { + steps { + container('mvn') { + withCredentials([file(credentialsId: 'mavenSettings.xml', variable: 'FILE')]) { + sh 'mvn --settings $FILE clean install # -Dmaven.test.skip=true' + } + dockerBuildAndPushToRegistry "${NAMESPACE}/${APP_NAME}", [IMAGE_BUILD_TAG] + } + } + } + stage("Helm chart") { + steps { + container('helm') { + helmBuildAndPushToRegistry HELM_CHART_NAME + } + } + } + stage("Approval: Deploy to staging ?") { + steps { + slackSend channel: SLACK_CHANNEL, message: "$APP_NAME: build #$BUILD_NUMBER ready to deploy to `STAGING`, approval required: $BUILD_URL (24h)" + + timeout(time: 24, unit: 'HOURS') { input 'Deploy to staging ?' } + } + post { failure { echo 'Deploy aborted for build #...' }} + } + stage("Deploy to staging") { + steps { + slackSend channel: SLACK_CHANNEL, message: "$APP_NAME: deploying build #$BUILD_NUMBER to `STAGING`" + deployHelmTo "staging", NAMESPACE + } + } + stage("Approval: Deploy to production ?") { + steps { + slackSend channel: SLACK_CHANNEL, message: "$APP_NAME: build #$BUILD_NUMBER ready to deploy to `PRODUCTION`, approval required: $BUILD_URL (24h)" + + timeout(time: 7, unit: 'DAYS') { input 'Deploy to production ?' } + } + post { failure { echo 'Deploy aborted for build #...' }} + } + stage('Tagging release') { + steps { + container("mvn") { + // Updating the "latest tag" + dockerTagLatestAndPushToRegistry "${NAMESPACE}/${APP_NAME}", IMAGE_BUILD_TAG + } + } + } + /* + stage('Deploy release to canary') { + steps { + slackSend channel: SLACK_CHANNEL, message: "$APP_NAME: deploying build #$BUILD_NUMBER to `PRODUCTION` (canary)" + echo "deploy to canary" + } + } + */ + stage("Deploy to production") { + steps { + slackSend channel: SLACK_CHANNEL, message: "$APP_NAME: deploying build #$BUILD_NUMBER to `PRODUCTION`" + + deployHelmTo "prod", NAMESPACE + } + } + } + } + } +} diff --git a/README.md b/README.md index df97891d9104de4ca91728f207a9696b34933293..a75e0ec4d2065ea36e3e35ad1612abfc0a64e12a 100644 --- a/README.md +++ b/README.md @@ -34,26 +34,47 @@ The generated artifacts are then referenced from the .proto repo as Maven depend Unit tests ``` -# How to build -1. Make sure there's Java 10 installed. -2. Get proper Maven settings.xml file and place it under ~/.m2 directory. This is needed because of the proto artifactory repo. -3. Run `mvn clean install` -4. (Optional, developers only) If you're using Eclipse, make sure you are on Eclipse Photon version at least. +# SetUp info for developers -# How to run -1. Make sure there's Cassandra reachable somewhere within your network. -For developers: the 'dev' profile (src/main/resources/application-dev.yml) assumes Cassandra will be reachable on localhost:9042. -For production: few environment variables should be set (see src/main/resources/application-production.yml). + For starting current service you must do next: + 1. Install JDK 10 for project and JDK 8 for Cassandra DB + 2. Install Cassandra + a. If you use a Windows machine, you must save in JAVA_HOME variable path to jdk8. + Install python 2.7. + Download 7z or other archive manager and extract Cassandra's files to a necessary directory + Delete from cassandra.bat file -XX:+UseParNewGC^ . + Start cassandra.bat + b. With Linux, we have the same behavior + 3. Install maven and add to .m2 folder settings.xml (ping your teamLead with this issue) + 4. Set to VM options dev profile " -Dspring.profiles.active=dev" + 5. Start application + If you're using Eclipse, make sure you are on Eclipse Photon version at least. -2. Use your IDE to start the Spring Boot Application (remember to set the 'Dev' profile) or run the Java application as: -`java -jar account-service.jar --spring.profiles.active=dev` +# SetUp for DevOps -# Build the Docker image +## How to build + 1. Make sure there's Java 10 installed. + 2. Get proper Maven settings.xml file and place it under ~/.m2 directory. This is needed because of the proto artifactory repo. + 3. Run `mvn clean install` + +## How to run + Make sure there's Cassandra reachable somewhere within your network. + For production: few environment variables should be set (see src/main/resources/application-production.yml). + + +## Build the Docker image ``` docker build -t account-service . ``` -# Run the containerized application +## Run the containerized application ``` docker run --rm -ti -p 8080:8080 -p 6565:6565 account-service ``` + +# Known issues + + (NY_3728) When starting the microservice account-service we get several warning messages due to a release mismatch with the groovy logging jar. + One possible but not suitable solution is use a VM argument "--illegal-access=deny". + Another proper solution is to upgrade the jar which should be done at a later date. + diff --git a/charts/account-service/.helmignore b/charts/account-service/.helmignore new file mode 100644 index 0000000000000000000000000000000000000000..f0c13194444163d1cba5c67d9e79231a62bc8f44 --- /dev/null +++ b/charts/account-service/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/account-service/Chart.yaml b/charts/account-service/Chart.yaml new file mode 100644 index 0000000000000000000000000000000000000000..15646489e1bf3df41f8d059527e6eae14ea613dd --- /dev/null +++ b/charts/account-service/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: Deployment of the nynja account service. +name: account-service +version: 0.1.0 diff --git a/charts/account-service/templates/00-label.yaml b/charts/account-service/templates/00-label.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dd5d17afc13ed355afb9d48f5d41bb161f685ccc --- /dev/null +++ b/charts/account-service/templates/00-label.yaml @@ -0,0 +1,32 @@ +# This hook depends on helm creating the target namespace if it doesn't exist +# before the hook is called. This is the case on Helm v2.9.1 +apiVersion: batch/v1 +kind: Job +metadata: + name: enable-istio-injection-{{ .Release.Namespace }} + namespace: kube-system + labels: + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + app.kubernetes.io/managed-by: {{.Release.Service | quote }} + app.kubernetes.io/instance: {{.Release.Name | quote }} + helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: hook-before-creation,hook-succeeded +spec: + template: + spec: + containers: + - name: labeler + image: gcr.io/google_containers/hyperkube:v1.9.7 + command: + - kubectl + - label + - --overwrite + - ns + - {{ .Release.Namespace }} + - istio-injection=enabled + restartPolicy: Never + # use tiller service account since it should have permissions to do namespace labeling + serviceAccountName: tiller diff --git a/charts/account-service/templates/_helpers.tpl b/charts/account-service/templates/_helpers.tpl new file mode 100644 index 0000000000000000000000000000000000000000..1befa9784b266cb2340e11a5c76065abfb26c88d --- /dev/null +++ b/charts/account-service/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "account-service.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "account-service.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "account-service.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/charts/account-service/templates/deployment.yaml b/charts/account-service/templates/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e2293f5fe361b03c0269bb8b551565de4320f4a1 --- /dev/null +++ b/charts/account-service/templates/deployment.yaml @@ -0,0 +1,72 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ template "account-service.fullname" . }} + labels: + app: {{ template "account-service.name" . }} + chart: {{ template "account-service.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "account-service.name" . }} + release: {{ .Release.Name }} + template: + metadata: + annotations: + sidecar.istio.io/inject: "true" + labels: + app: {{ template "account-service.name" . }} + release: {{ .Release.Name }} + spec: + containers: + - name: {{ template "account-service.name" . }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - containerPort: {{ .Values.ports.containerPort.http }} + name: http + - containerPort: {{ .Values.ports.containerPort.grpc }} + name: grpc + readinessProbe: + exec: + command: + - /bin/sh + - -c + - curl --silent http://localhost:{{ .Values.ports.containerPort.http }}/actuator/health 2>&1 | grep UP || exit 1 + successThreshold: 1 + failureThreshold: 10 + initialDelaySeconds: 60 + periodSeconds: 5 + timeoutSeconds: 5 + livenessProbe: + httpGet: + path: /actuator/health + port: {{ .Values.ports.containerPort.http }} + successThreshold: 1 + failureThreshold: 10 + initialDelaySeconds: 60 + periodSeconds: 5 + timeoutSeconds: 5 + env: + ## Container ports. + - name: HTTP_SERVER_PORT + value: {{ .Values.ports.containerPort.http | quote }} + - name: GRPC_SERVER_PORT + value: {{ .Values.ports.containerPort.grpc | quote }} + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/charts/account-service/templates/envoy-grpc-web.yaml b/charts/account-service/templates/envoy-grpc-web.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7462b223dd319c249e8d2e515e99095de723b225 --- /dev/null +++ b/charts/account-service/templates/envoy-grpc-web.yaml @@ -0,0 +1,21 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: {{ template "account-service.fullname" . }}-grpc-web + labels: + app: {{ template "account-service.name" . }} + chart: {{ template "account-service.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + workloadLabels: + app: {{ template "account-service.name" . }} + filters: + - listenerMatch: + portNumber: {{ .Values.ports.containerPort.grpc }} + listenerType: SIDECAR_INBOUND + filterName: envoy.grpc_web + filterType: HTTP + filterConfig: {} + insertPosition: + index: FIRST diff --git a/charts/account-service/templates/service.yaml b/charts/account-service/templates/service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5eef5b8199e5f312a6199cbc88d13fa62f93a4f6 --- /dev/null +++ b/charts/account-service/templates/service.yaml @@ -0,0 +1,22 @@ +kind: Service +apiVersion: v1 +metadata: + name: {{ template "account-service.fullname" . }} + labels: + app: {{ template "account-service.name" . }} + chart: {{ template "account-service.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + selector: + app: {{ template "account-service.name" . }} + release: {{ .Release.Name }} + ports: + - protocol: TCP + port: {{ .Values.ports.containerPort.http }} + targetPort: {{ .Values.ports.containerPort.http }} + name: http-account + - protocol: TCP + port: {{ .Values.ports.containerPort.grpc }} + targetPort: {{ .Values.ports.containerPort.grpc }} + name: grpc-account diff --git a/charts/account-service/templates/virtualservice.yaml b/charts/account-service/templates/virtualservice.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d3301e848515bf6ea4982394a829599327d6f38f --- /dev/null +++ b/charts/account-service/templates/virtualservice.yaml @@ -0,0 +1,42 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: {{ template "account-service.fullname" . }} + labels: + app: {{ template "account-service.name" . }} + chart: {{ template "account-service.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + gateways: + {{- range .Values.gateway.selector }} + - {{ . }} + {{- end }} + hosts: + {{- range .Values.gateway.hosts }} + - {{ . }} + {{- end }} + http: + - match: + - uri: + prefix: / + route: + - destination: + host: {{ template "account-service.fullname" . }} + port: + number: {{ .Values.ports.containerPort.grpc }} + corsPolicy: + allowOrigin: + {{- range .Values.corsPolicy.allowOrigin }} + - {{ . }} + {{- end }} + allowMethods: + {{- range .Values.corsPolicy.allowMethods}} + - {{ . }} + {{- end }} + allowCredentials: {{ .Values.corsPolicy.allowCredentials }} + allowHeaders: + {{- range .Values.corsPolicy.allowHeaders }} + - {{ . }} + {{- end }} + maxAge: {{ .Values.corsPolicy.maxAge }} diff --git a/charts/account-service/values.yaml b/charts/account-service/values.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9428e11b241c85bd9f2417a6869ac99eba96dfe2 --- /dev/null +++ b/charts/account-service/values.yaml @@ -0,0 +1,38 @@ +replicaCount: 1 + +image: + repository: eu.gcr.io/nynja-ci-201610/nynja-account/nynja-account-service + tag: stable + pullPolicy: IfNotPresent + +gateway: + selector: + - api-gateway.default.svc.cluster.local + hosts: + +resources: + limits: + cpu: 1 + memory: 1Gi + requests: + cpu: 500m + memory: 512Mi + +ports: + containerPort: + http: + grpc: + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +corsPolicy: + allowOrigin: + allowMethods: + allowCredentials: + allowHeaders: + maxAge: + diff --git a/pom.xml b/pom.xml index 2f5577d9f6a245c126f622e5b6c86c22db4954b6..ad665e6ff21fe253fac125dbf21dce25e09a13cc 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ UTF-8 UTF-8 - 1.10 + 10 2.3.2 @@ -101,7 +101,7 @@ libs-snapshot-local.biz.nynja.protos - blueprint-java-intracoldev + account-service-intracoldev 1.0-SNAPSHOT @@ -111,18 +111,50 @@ + + com.googlecode.libphonenumber + libphonenumber + 8.9.12 + + com.fasterxml.jackson.core jackson-databind - 2.9.5 + 2.9.5 com.fasterxml.jackson.core jackson-core - 2.9.5 + 2.9.5 + + + + org.codehaus.groovy + groovy-all + + + + io.micrometer + micrometer-core + + io.micrometer + micrometer-registry-prometheus + + + + org.apache.commons + commons-lang3 + 3.8.1 + + + + com.sun.mail + javax.mail + 1.6.2 + diff --git a/releases/dev/account-service.yaml b/releases/dev/account-service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8b4d6d31afc40ca620bcc7e2964f5eaaf9bcdc70 --- /dev/null +++ b/releases/dev/account-service.yaml @@ -0,0 +1,55 @@ +kind: HelmRelease +metadata: + name: account-service + namespace: account +spec: + chart: + name: account-service + values: + replicaCount: 1 + + image: + repository: ${IMAGE_NAME} + tag: ${IMAGE_BUILD_TAG} + + gateway: + selector: + - api-gateway.default.svc.cluster.local + hosts: + - account.dev-eu.nynja.net + + resources: + limits: + cpu: 1 + memory: 1500Mi + requests: + cpu: 500m + memory: 1000Mi + + ports: + containerPort: + http: 8080 + grpc: 6565 + + # CORS policy + corsPolicy: + allowOrigin: + - http://localhost:3000 + - https://localhost + - https://localhost/grpc/ + - http://10.191.224.180:3000 + - https://localhost:8080 + - https://127.0.0.1:8080 + - https://web.dev-eu.nynja.net + - https://web.staging.nynja.net + - https://web.nynja.net + allowMethods: + - POST + - GET + - OPTIONS + allowCredentials: false + allowHeaders: + - content-type + - x-grpc-web + maxAge: "600s" + diff --git a/releases/prod/account-service.yaml b/releases/prod/account-service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..cf66d1519f268531fc744b59825d38c5ef6f772a --- /dev/null +++ b/releases/prod/account-service.yaml @@ -0,0 +1,17 @@ +kind: HelmRelease +metadata: + name: account-service + namespace: account +spec: + chart: + name: account-service + values: + replicaCount: 3 + image: + repository: ${IMAGE_NAME} + tag: ${IMAGE_BUILD_TAG} + gateway: + selector: + - api-gateway.default.svc.cluster.local + hosts: + - account.nynja.net diff --git a/releases/staging/account-service.yaml b/releases/staging/account-service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8a149a61960b085dd314a4d84ca307cc3c66d467 --- /dev/null +++ b/releases/staging/account-service.yaml @@ -0,0 +1,17 @@ +kind: HelmRelease +metadata: + name: account-service + namespace: account +spec: + chart: + name: account-service + values: + replicaCount: 2 + image: + repository: ${IMAGE_NAME} + tag: ${IMAGE_BUILD_TAG} + gateway: + selector: + - api-gateway.default.svc.cluster.local + hosts: + - account.staging.nynja.net diff --git a/setup/intracol-code-formatter.xml b/setup/intracol-code-formatter.xml new file mode 100644 index 0000000000000000000000000000000000000000..06b04e2b9e824eaf5d96499005dc6a92b045cdd6 --- /dev/null +++ b/setup/intracol-code-formatter.xml @@ -0,0 +1,587 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/biz/nynja/account/grpc/Application.java b/src/main/java/biz/nynja/account/Application.java similarity index 92% rename from src/main/java/biz/nynja/account/grpc/Application.java rename to src/main/java/biz/nynja/account/Application.java index 79f40bb26b6ad5d357de400ddd16e26caef58460..f19c3d10434c5e8f31d924d56ebdc731fec98c02 100644 --- a/src/main/java/biz/nynja/account/grpc/Application.java +++ b/src/main/java/biz/nynja/account/Application.java @@ -1,7 +1,7 @@ /** * Copyright (C) 2018 Nynja Inc. All rights reserved. */ -package biz.nynja.account.grpc; +package biz.nynja.account; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/src/main/java/biz/nynja/account/StartupScriptsListener.java b/src/main/java/biz/nynja/account/StartupScriptsListener.java new file mode 100644 index 0000000000000000000000000000000000000000..ac797ab029f730ba455206115828361f22ceda77 --- /dev/null +++ b/src/main/java/biz/nynja/account/StartupScriptsListener.java @@ -0,0 +1,67 @@ +package biz.nynja.account; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import com.datastax.driver.core.Session; + +import biz.nynja.account.configuration.CassandraConfig; + +/** + * This acts as {@link CassandraConfig} startupScripts executor + * but activated after the spring has setup the needed tables though JPA + * @author dragomir.todorov + * + */ +@Component +public class StartupScriptsListener { + + private String keyspace; + + @Autowired + private Session session; + + @EventListener(ContextRefreshedEvent.class) + public void contextRefreshedEvent() { + keyspace = session.getLoggedKeyspace(); + + for (String script : getStartupScripts()) { + session.execute(script); + } + } + + private List getStartupScripts() { + String scriptAccountViewByProfileId = "CREATE MATERIALIZED VIEW IF NOT EXISTS " + keyspace + + ".accountbyprofileid AS SELECT * FROM account " + "WHERE profileid IS NOT NULL " + + "PRIMARY KEY (profileid, accountid);"; + + String scriptAccountViewByAuthProvider = "CREATE MATERIALIZED VIEW IF NOT EXISTS " + keyspace + + ".accountbyauthenticationprovider AS SELECT * FROM account " + + "WHERE authenticationprovider IS NOT NULL " + "PRIMARY KEY (authenticationprovider, accountid);"; + + String scriptAccountViewByAccountName = "CREATE MATERIALIZED VIEW IF NOT EXISTS " + keyspace + + ".accountbyaccountname AS SELECT * FROM account " + "WHERE accountname IS NOT NULL " + + "PRIMARY KEY (accountname, accountid);"; + + String scriptAccountViewByUsername = "CREATE MATERIALIZED VIEW IF NOT EXISTS " + keyspace + + ".accountbyusername AS SELECT * FROM account " + "WHERE username IS NOT NULL " + + "PRIMARY KEY (username, accountid);"; + + String scriptPendingAccountViewByAuthenticationProvider = "CREATE MATERIALIZED VIEW IF NOT EXISTS " + keyspace + + ".pendingaccountbyauthenticationprovider AS SELECT * FROM pendingaccount " + "WHERE authenticationprovider IS NOT NULL " + + "PRIMARY KEY (authenticationprovider, accountid);"; + + String scriptAccountViewByQrCode = "CREATE MATERIALIZED VIEW IF NOT EXISTS " + keyspace + + ".accountbyqrcode AS SELECT * FROM account " + "WHERE qrcode IS NOT NULL " + + "PRIMARY KEY (qrcode, accountid);"; + + return Arrays.asList(scriptAccountViewByProfileId, scriptAccountViewByAuthProvider, + scriptAccountViewByAccountName, scriptAccountViewByUsername, + scriptPendingAccountViewByAuthenticationProvider, scriptAccountViewByQrCode); + } +} \ No newline at end of file diff --git a/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java b/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java new file mode 100644 index 0000000000000000000000000000000000000000..c1260f786a8d5285ab5adffa3df7e95c6ec8f171 --- /dev/null +++ b/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.codecs; + +import java.nio.ByteBuffer; + +import com.datastax.driver.core.ProtocolVersion; +import com.datastax.driver.core.TypeCodec; +import com.datastax.driver.core.UDTValue; +import com.datastax.driver.core.UserType; +import com.datastax.driver.core.exceptions.InvalidTypeException; + +import biz.nynja.account.models.AuthenticationProvider; + + +public class AuthenticationProviderCodec extends TypeCodec { + + private final TypeCodec innerCodec; + + private final UserType userType; + + public AuthenticationProviderCodec(TypeCodec innerCodec, Class javaType) { + super(innerCodec.getCqlType(), javaType); + this.innerCodec = innerCodec; + this.userType = (UserType) innerCodec.getCqlType(); + } + + @Override + public ByteBuffer serialize(AuthenticationProvider value, ProtocolVersion protocolVersion) + throws InvalidTypeException { + return innerCodec.serialize(toUDTValue(value), protocolVersion); + } + + @Override + public AuthenticationProvider deserialize(ByteBuffer bytes, ProtocolVersion protocolVersion) + throws InvalidTypeException { + return toAuthenticationProvider(innerCodec.deserialize(bytes, protocolVersion)); + } + + @Override + public AuthenticationProvider parse(String value) throws InvalidTypeException { + return value == null || value.isEmpty() || value.equals("NULL") ? null + : toAuthenticationProvider(innerCodec.parse(value)); + } + + @Override + public String format(AuthenticationProvider value) throws InvalidTypeException { + return value == null ? null : innerCodec.format(toUDTValue(value)); + } + + protected AuthenticationProvider toAuthenticationProvider(UDTValue value) { + if (value == null) { + return null; + } else { + AuthenticationProvider authProvider = new AuthenticationProvider(); + authProvider.setType(value.getString("type")); + authProvider.setValue(value.getString("value")); + return authProvider; + } + } + + protected UDTValue toUDTValue(AuthenticationProvider value) { + return value == null ? null + : userType.newValue().setString("type", value.getType()).setString("value", value.getValue()); + } +} diff --git a/src/main/java/biz/nynja/account/components/AccountServiceHelper.java b/src/main/java/biz/nynja/account/components/AccountServiceHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..c47fa789bc087d4b2cb4b4839c5007de28c3ad57 --- /dev/null +++ b/src/main/java/biz/nynja/account/components/AccountServiceHelper.java @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.components; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import biz.nynja.account.models.Account; +import biz.nynja.account.models.AccountByAuthenticationProvider; +import biz.nynja.account.repositories.AccountByAuthenticationProviderRepository; + +@Service +public class AccountServiceHelper { + @Autowired + private AccountByAuthenticationProviderRepository accountByAuthenticationProviderRepository; + + public Account getAccountByAuthenticationProviderHelper(String authenticationIdentifier, + String type) { + + List accounts = accountByAuthenticationProviderRepository + .findAllByAuthenticationProvider(authenticationIdentifier); + + if (accounts.isEmpty()) { + return null; + } + + // We retrieve accounts by authentication provider identifier from DB where both authentication provider + // identifier and type uniquely identify an account. + // For this reason we need to filter results by authentication provider type. + for (AccountByAuthenticationProvider account : accounts) { + if (account.getAuthenticationProviderType().equals(type)) { + return account.toAccount(); + } + } + + return null; + } +} diff --git a/src/main/java/biz/nynja/account/components/PendingAccountValidator.java b/src/main/java/biz/nynja/account/components/PendingAccountValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..09c4aae6be2b18492f4da27dd1f97cc96a426c7a --- /dev/null +++ b/src/main/java/biz/nynja/account/components/PendingAccountValidator.java @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.components; + +import org.springframework.stereotype.Service; + +import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; +import biz.nynja.account.grpc.CreatePendingAccountRequest; +import biz.nynja.account.grpc.ErrorResponse.Cause; + +@Service +public class PendingAccountValidator { + + private Validator validator; + + public PendingAccountValidator(Validator validator) { + this.validator = validator; + } + + public Cause validateCreatePendingAccountRequest(CreatePendingAccountRequest request) { + return validator.validateAuthProvider(request.getAuthenticationType(), request.getAuthenticationProvider()); + } + + public Cause validateCompletePendingAccountCreationRequest(CompletePendingAccountCreationRequest request) { + if (request.getAccountId() == null || request.getAccountId().trim().isEmpty()) { + return Cause.MISSING_ACCOUNT_ID; + } + + if (request.getFirstName() != null && request.getFirstName().trim().isEmpty()) { + return Cause.MISSING_FIRST_NAME; + } else if (!validator.isFirstNameValid(request.getFirstName())) { + return Cause.INVALID_FIRST_NAME; + } + + if (request.getLastName() != null && !request.getLastName().trim().isEmpty() + && !validator.isLastNameValid(request.getLastName())) { + return Cause.INVALID_LAST_NAME; + } + + if (request.getUsername() != null && !request.getUsername().trim().isEmpty() + && !validator.isUsernameValid(request.getUsername())) { + return Cause.USERNAME_INVALID; + } + + if (request.getAccountName() != null && !request.getAccountName().trim().isEmpty() + && !validator.isAccountNameValid(request.getAccountName())) { + return Cause.ACCOUNT_NAME_INVALID; + } + + return null; + } + +} diff --git a/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java new file mode 100644 index 0000000000000000000000000000000000000000..91b9b32575dc5804591380f851ee8366510e02e4 --- /dev/null +++ b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.components; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.TypeCodec; +import com.datastax.driver.core.UDTValue; +import com.datastax.driver.core.UserType; + +import biz.nynja.account.codecs.AuthenticationProviderCodec; +import biz.nynja.account.configuration.CassandraConfig; +import biz.nynja.account.models.AuthenticationProvider; + +@Configuration +public class PreparedStatementsCache { + + private final CassandraConfig cassandraConfig; + + private final Session session; + + private static Map statementsCache; + + @Autowired + public PreparedStatementsCache(Session session, CassandraConfig cassandraConfig) { + this.session = session; + this.cassandraConfig = cassandraConfig; + } + + @PostConstruct + public void init() { + statementsCache = new ConcurrentHashMap<>(); + registerAuthenticationProviderCodec(); + } + + private Map getPreparedStatementsCache() { + return statementsCache; + } + + public PreparedStatement getPreparedStatement(String cql) { + if (getPreparedStatementsCache().containsKey(cql)) { + return getPreparedStatementsCache().get(cql); + } else { + PreparedStatement statement = session.prepare(cql); + getPreparedStatementsCache().put(cql, statement); + return statement; + } + } + + private void registerAuthenticationProviderCodec() { + UserType authenticationProviderType = session.getCluster().getMetadata() + .getKeyspace(cassandraConfig.getConfiguredKeyspaceName()).getUserType("authenticationprovider"); + TypeCodec authenticationProviderTypeCodec = session.getCluster().getConfiguration().getCodecRegistry() + .codecFor(authenticationProviderType); + + AuthenticationProviderCodec addressCodec = new AuthenticationProviderCodec(authenticationProviderTypeCodec, + AuthenticationProvider.class); + session.getCluster().getConfiguration().getCodecRegistry().register(addressCodec); + } +} diff --git a/src/main/java/biz/nynja/account/components/StatementsPool.java b/src/main/java/biz/nynja/account/components/StatementsPool.java new file mode 100644 index 0000000000000000000000000000000000000000..ba8c9d66456df3ab3381b998c21acb7c3a1a562d --- /dev/null +++ b/src/main/java/biz/nynja/account/components/StatementsPool.java @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.components; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.datastax.driver.core.BoundStatement; + +import biz.nynja.account.models.AuthenticationProvider; + +@Service +public class StatementsPool { + + private final PreparedStatementsCache preparedStatementsCache; + + @Autowired + public StatementsPool(PreparedStatementsCache preparedStatementsCache) { + this.preparedStatementsCache = preparedStatementsCache; + } + + public BoundStatement addAuthenticationProviderToProfile(UUID profileId, AuthenticationProvider authProvider) { + String cql = "UPDATE profile SET authenticationproviders = authenticationproviders + ? WHERE profileid = ? ;"; + Set toBeAdded = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(authProvider))); + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(toBeAdded, profileId); + return bound; + } + + public BoundStatement insertProfileByAuthenticationProvider(UUID profileId, String authPovider, + String authProviderType) { + String cql = "INSERT INTO profilebyauthenticationprovider (authenticationprovider, authenticationprovidertype, profileid) VALUES (?, ?, ?) ;"; + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(authPovider, authProviderType, + profileId); + return bound; + } + + public BoundStatement deleteAuthenicationProviderFromProfile(UUID profileId, AuthenticationProvider authProvider) { + String cql = "UPDATE profile SET authenticationproviders = authenticationproviders - ? WHERE profileid = ? ;"; + Set toBeDeleted = Collections + .unmodifiableSet(new HashSet<>(Arrays.asList(authProvider))); + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(toBeDeleted, profileId); + return bound; + } + + public BoundStatement deleteProfileByAuthenticationProvider(String authProvider, String authProviderType) { + String cql = "DELETE FROM profilebyauthenticationprovider where authenticationprovider = ? and authenticationprovidertype = ? ;"; + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(authProvider, authProviderType); + return bound; + } + + public BoundStatement deleteCreationProviderFromAccount(UUID accountId) { + String cql = "DELETE authenticationprovidertype, authenticationprovider FROM account WHERE accountid = ? ;"; + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(accountId); + return bound; + } +} diff --git a/src/main/java/biz/nynja/account/components/Validator.java b/src/main/java/biz/nynja/account/components/Validator.java new file mode 100644 index 0000000000000000000000000000000000000000..c48ff7dbb976c2806d2babd64097421c92f7f06c --- /dev/null +++ b/src/main/java/biz/nynja/account/components/Validator.java @@ -0,0 +1,275 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.components; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import biz.nynja.account.grpc.AddAuthenticationProviderRequest; +import biz.nynja.account.grpc.AuthenticationType; +import biz.nynja.account.grpc.ContactDetails; +import biz.nynja.account.grpc.ContactType; +import biz.nynja.account.grpc.Date; +import biz.nynja.account.grpc.DeleteAuthenticationProviderRequest; +import biz.nynja.account.grpc.ErrorResponse.Cause; +import biz.nynja.account.grpc.UpdateAccountRequest; +import biz.nynja.account.grpc.UpdateProfileRequest; +import biz.nynja.account.phone.PhoneNumberValidator; + +/** + * Component which contains all validation methods. + */ + +@Component +public class Validator { + + private static final Logger logger = LoggerFactory.getLogger(Validator.class); + + private static final int MIN_ACCOUNT_NAME_LENGTH = 2; + private static final int MAX_ACCOUNT_NAME_LENGTH = 32; + private static final int MIN_FIRST_NAME_LENGTH = 2; + private static final int MAX_FIRST_NAME_LENGTH = 32; + private static final int MIN_LAST_NAME_LENGTH = 0; + private static final int MAX_LAST_NAME_LENGTH = 32; + + private PhoneNumberValidator phoneValidator; + + public Validator(PhoneNumberValidator phoneValidator) { + this.phoneValidator = phoneValidator; + + } + + public boolean isUsernameValid(String username) { + + logger.debug("Checking username: {}", username); + + final String USERNAME_PATTERN = "^[A-Za-z0-9_]{2,32}$"; + + Pattern pattern = Pattern.compile(USERNAME_PATTERN); + Matcher matcher = pattern.matcher(username); + + boolean isValid = matcher.matches(); + logger.debug("Username: {} is valid: {}", username, isValid); + + return isValid; + } + + public boolean isEmailValid(String email) { + boolean result = true; + logger.debug("Checking email: {}", email); + + try { + InternetAddress emailAddr = new InternetAddress(email); + emailAddr.validate(); + } catch (AddressException ex) { + result = false; + } + logger.debug("Email: {} is valid: {}", email, result); + return result; + } + + public boolean isFirstNameValid(String firstName) { + logger.debug("Checking First Name: {}", firstName); + + int len = firstName.length(); + boolean isValid = MIN_FIRST_NAME_LENGTH <= len && len <= MAX_FIRST_NAME_LENGTH; + + logger.debug("First Name: {} is valid: {}", firstName, isValid); + return isValid; + } + + public boolean isLastNameValid(String lastName) { + logger.debug("Checking Last Name: {}", lastName); + + int len = lastName.length(); + boolean isValid = MIN_LAST_NAME_LENGTH <= len && len <= MAX_LAST_NAME_LENGTH; + + logger.debug("Last Name: {} is valid: {}", lastName, isValid); + return isValid; + } + + public boolean isValidUsername(String username) { + if (username == null) { + return false; + } + return username.matches("[a-zA-Z0-9_]{1,32}"); + } + + boolean isAccountNameValid(String accountName) { + logger.debug("Checking Account Name: {}", accountName); + + int len = accountName.length(); + boolean isValid = MIN_ACCOUNT_NAME_LENGTH <= len && len <= MAX_ACCOUNT_NAME_LENGTH; + + logger.debug("First Name: {} is valid: {}", accountName, isValid); + return isValid; + } + + public boolean isValidUuid(String id) { + return id.matches("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); + } + + public Cause validateAuthProvider(AuthenticationType type, String authProvider) { + if (authProvider == null || authProvider.trim().isEmpty()) { + return Cause.MISSING_AUTH_PROVIDER_IDENTIFIER; + } + switch (type) { + case MISSING_TYPE: + return Cause.MISSING_AUTH_PROVIDER_TYPE; + case PHONE: + // We expect to receive phone number in the following format : ":" + String[] provider = authProvider.split(":"); + if (provider == null || provider.length != 2) { + return Cause.PHONE_NUMBER_INVALID; + } + if (!phoneValidator.isPhoneNumberValid(provider[1], provider[0])) { + return Cause.PHONE_NUMBER_INVALID; + } + break; + case EMAIL: + if (!isEmailValid(authProvider)) { + return Cause.EMAIL_INVALID; + } + break; + default: + break; + } + return null; + } + + public Optional> validateContactInfo(ContactType type, String contactInfoValue) { + if (contactInfoValue == null || contactInfoValue.trim().isEmpty()) { + return Optional + .of(new ImmutablePair<>(Cause.MISSING_CONTACT_INFO_IDENTIFIER, "Missing contact info identifier")); + } + switch (type) { + case MISSING_CONTACT_TYPE: + return Optional.of(new ImmutablePair<>(Cause.MISSING_CONTACT_INFO_TYPE, "Missing contact info type")); + case PHONE_CONTACT: + // We expect to receive phone number in the following format : ":" + String[] provider = contactInfoValue.split(":"); + if (provider == null || provider.length != 2) { + return Optional.of(new ImmutablePair<>(Cause.PHONE_NUMBER_INVALID, "Invalid phone number")); + } + if (!phoneValidator.isPhoneNumberValid(provider[1], provider[0])) { + return Optional.of(new ImmutablePair<>(Cause.PHONE_NUMBER_INVALID, "Invalid phone number")); + } + break; + case EMAIL_CONTACT: + if (!isEmailValid(contactInfoValue)) { + return Optional.of(new ImmutablePair<>(Cause.EMAIL_INVALID, "Invalid email")); + } + break; + default: + break; + } + return Optional.empty(); + } + + public boolean validateBirthdayIsSet(Date birthday) { + return (birthday != null && birthday.getYear() != 0 && birthday.getMonth() != 0 && birthday.getDay() != 0); + } + + public Cause validateUpdateAccountRequest(UpdateAccountRequest request) { + + if (request.getUsername() != null && !request.getUsername().trim().isEmpty() + && !isUsernameValid(request.getUsername())) { + return Cause.USERNAME_INVALID; + } + + if (request.getFirstName() != null && request.getFirstName().trim().isEmpty()) { + return Cause.MISSING_FIRST_NAME; + } else if (!isFirstNameValid(request.getFirstName())) { + return Cause.INVALID_FIRST_NAME; + } + + if (request.getLastName() != null && !request.getLastName().trim().isEmpty() + && !isLastNameValid(request.getLastName())) { + return Cause.INVALID_LAST_NAME; + } + + if (request.getAccountName() != null && !request.getAccountName().trim().isEmpty() + && !isAccountNameValid(request.getAccountName())) { + return Cause.ACCOUNT_NAME_INVALID; + } + + if (validateBirthdayIsSet(request.getBirthday())) { + try { + LocalDate.of(request.getBirthday().getYear(), request.getBirthday().getMonth(), + request.getBirthday().getDay()); + } catch (DateTimeException e) { + return Cause.INVALID_BIRTHDAY_DATE; + } + } + + return null; + } + + public Cause validateUpdateProfileRequest(UpdateProfileRequest request) { + if (!isValidUuid(request.getProfileId())) { + return Cause.INVALID_PROFILE_ID; + } + return null; + } + + public Cause validateAddAuthenticationProviderRequest(AddAuthenticationProviderRequest request) { + if (!isValidUuid(request.getProfileId())) { + return Cause.INVALID_PROFILE_ID; + } + return validateAuthProvider(request.getAuthenticationProvider().getAuthenticationType(), + request.getAuthenticationProvider().getAuthenticationProvider()); + } + + public Optional> validateEditContactInfoRequest(String accountId, + ContactDetails oldContactInfoDetails, ContactDetails editedContactInfoDetails) { + Optional> validationResultOldContactInfo = validateContactInfoRequest(accountId, + oldContactInfoDetails); + if (validationResultOldContactInfo.isPresent()) { + return validationResultOldContactInfo; + } + return validateContactInfoRequest(accountId, editedContactInfoDetails); + } + + public Optional> validateContactInfoRequest(String accountId, + ContactDetails contactDetails) { + if ((accountId == null) || (accountId.isEmpty())) { + return Optional.of(new ImmutablePair<>(Cause.MISSING_ACCOUNT_ID, "Missing account id")); + } + if (contactDetails.getTypeValue() == 0) { + return Optional.of(new ImmutablePair<>(Cause.MISSING_CONTACT_INFO_TYPE, "Missing contact info type")); + + } + if (contactDetails.getValue() == null || contactDetails.getValue().isEmpty()) { + return Optional + .of(new ImmutablePair<>(Cause.MISSING_CONTACT_INFO_IDENTIFIER, "Missing contact info identifier")); + } + if (!isValidUuid(accountId)) { + return Optional.of(new ImmutablePair<>(Cause.INVALID_ACCOUNT_ID, "Invalid account id")); + } + return validateContactInfo(contactDetails.getType(), contactDetails.getValue()); + } + + public Cause validateDeleteAuthenticationProviderRequest(DeleteAuthenticationProviderRequest request) { + if (!isValidUuid(request.getProfileId())) { + return Cause.INVALID_PROFILE_ID; + } + Cause cause = validateAuthProvider(request.getAuthenticationProvider().getAuthenticationType(), + request.getAuthenticationProvider().getAuthenticationProvider()); + if (cause != null) { + return cause; + } + return null; + } +} diff --git a/src/main/java/biz/nynja/account/configuration/CassandraConfig.java b/src/main/java/biz/nynja/account/configuration/CassandraConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..739d5063f39b8523254bf50a494cdee7e90b7e96 --- /dev/null +++ b/src/main/java/biz/nynja/account/configuration/CassandraConfig.java @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.configuration; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.cassandra.config.AbstractCassandraConfiguration; +import org.springframework.data.cassandra.config.SchemaAction; +import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification; +import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories; + +import biz.nynja.account.StartupScriptsListener; + +@Configuration +@EnableCassandraRepositories +@ConditionalOnMissingClass("org.springframework.test.context.junit4.SpringRunner") +public class CassandraConfig extends AbstractCassandraConfiguration { + + @Value("${spring.data.cassandra.keyspace-name}") + private String keyspace; + + @Override + protected String getKeyspaceName() { + return keyspace; + } + + @Value("${spring.data.cassandra.contact-points}") + private String contactPoints; + + @Override + protected String getContactPoints() { + return contactPoints; + } + + @Value("${spring.data.cassandra.port}") + private int port; + + @Override + protected int getPort() { + return port; + } + + @Override + public SchemaAction getSchemaAction() { + return SchemaAction.CREATE_IF_NOT_EXISTS; + } + + @Override + protected List getKeyspaceCreations() { + CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace(getKeyspaceName()) + .ifNotExists().withSimpleReplication(); + return Arrays.asList(specification); + } + + @Override + public String[] getEntityBasePackages() { + return new String[] { "biz.nynja.account.models" }; + } + + /** + * See {@link StartupScriptsListener} for scripts + * that require JPA annotated tables + */ + @Override + protected List getStartupScripts() { + return super.getStartupScripts(); + } + + public String getConfiguredKeyspaceName() { + return getKeyspaceName(); + } +} diff --git a/src/main/java/biz/nynja/account/grid/ag/AdminServiceImpl.java b/src/main/java/biz/nynja/account/grid/ag/AdminServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..535bd2cbc8425b4da8a8290f3e50cfd1a4c01a9a --- /dev/null +++ b/src/main/java/biz/nynja/account/grid/ag/AdminServiceImpl.java @@ -0,0 +1,92 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.grid.ag; + +import org.lognet.springboot.grpc.GRpcService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import biz.nynja.account.admin.grpc.AccountsAdminResponse; +import biz.nynja.account.admin.grpc.AccountsCount; +import biz.nynja.account.admin.grpc.AdminAccountServiceGrpc; +import biz.nynja.account.admin.grpc.CreateAccountRequest; +import biz.nynja.account.admin.grpc.EmptyRequest; +import biz.nynja.account.admin.grpc.GetAllAccountsRequest; +import biz.nynja.account.grpc.AccountResponse; +import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; +import biz.nynja.account.grpc.CreatePendingAccountRequest; +import biz.nynja.account.grpc.CreatePendingAccountResponse; +import biz.nynja.account.repositories.AccountRepository; +import biz.nynja.account.services.decomposition.AccountCreator; +import io.grpc.stub.StreamObserver; + +/** + * gRPC Admin service implementation.
+ * The service extends the protobuf generated class and overrides the needed methods. It also saves/retrieves the admin + * information. + */ +@GRpcService +public class AdminServiceImpl extends AdminAccountServiceGrpc.AdminAccountServiceImplBase { + + private static final Logger logger = LoggerFactory.getLogger(AdminServiceImpl.class); + + private AgGridService agGridService; + private AccountRepository accountRepository; + private final AccountCreator accountCreator; + + public AdminServiceImpl(AgGridService agGridService, AccountRepository accountRepository, + AccountCreator accountCreator) { + this.agGridService = agGridService; + this.accountRepository = accountRepository; + this.accountCreator = accountCreator; + } + + @Override + public void getAllAccounts(GetAllAccountsRequest request, StreamObserver responseObserver) { + + AccountsAdminResponse response = agGridService.getData(request.getEndRow(), request.getStartRow()); + + responseObserver.onNext(response); + responseObserver.onCompleted(); + return; + } + + @Override + public void getCountOfAllAccounts(EmptyRequest request, StreamObserver responseObserver) { + long count = accountRepository.count(); + responseObserver.onNext(AccountsCount.newBuilder().setCount(Math.toIntExact(count)).build()); + responseObserver.onCompleted(); + } + + @Override + public void createAccount(CreateAccountRequest request, StreamObserver responseObserver) { + + logger.info("Creating account from admin console..."); + logger.debug("Creating account from admin console: {} ...", request); + + CreatePendingAccountRequest pendingAccountRequest = CreatePendingAccountRequest.newBuilder() + .setAuthenticationProvider(request.getAuthenticationProvider()) + .setAuthenticationType(request.getAuthenticationType()).build(); + CreatePendingAccountResponse pendingAccountResponse = accountCreator + .retrieveCreatePendingAccountResponse(pendingAccountRequest); + + if (pendingAccountResponse.hasError()) { + responseObserver.onNext(AccountResponse.newBuilder().setError(pendingAccountResponse.getError()).build()); + responseObserver.onCompleted(); + return; + } + + CompletePendingAccountCreationRequest completePendingAccount = CompletePendingAccountCreationRequest + .newBuilder().setAccountId(pendingAccountResponse.getPendingAccountDetails().getAccountId()) + .setAvatar(request.getAvatar()).setAccountMark(request.getAccountMark()) + .setAccountName(request.getAccountName()).setFirstName(request.getFirstName()) + .setLastName(request.getLastName()).setUsername(request.getUsername()) + .addAllRoles(request.getRolesList()).setAccessStatus(request.getAccessStatus()) + .setQrCode(request.getQrCode()).build(); + + AccountResponse response = accountCreator.retrieveCompletePendingAccountResponse(completePendingAccount); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } +} diff --git a/src/main/java/biz/nynja/account/grid/ag/AgGridController.java b/src/main/java/biz/nynja/account/grid/ag/AgGridController.java new file mode 100644 index 0000000000000000000000000000000000000000..f7a14db4b288859231f58a369ba06ef9ebb51f55 --- /dev/null +++ b/src/main/java/biz/nynja/account/grid/ag/AgGridController.java @@ -0,0 +1,39 @@ +package biz.nynja.account.grid.ag; + +import java.util.HashMap; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import biz.nynja.account.grid.ag.request.GetRowsRequest; +import biz.nynja.account.repositories.AccountRepository; + +@RestController +public class AgGridController { + + private AgGridService agGridService; + + private AccountRepository accountRepository; + + @Autowired + public AgGridController(AgGridService accountDao, AccountRepository accountRepository) { + this.agGridService = accountDao; + this.accountRepository = accountRepository; + } + + /* @RequestMapping(method = RequestMethod.POST, value = "/getRows") + public ResponseEntity getRows(@RequestBody GetRowsRequest request) { + return ResponseEntity.ok(agGridService.getData(request)); + }*/ + + @RequestMapping(method = RequestMethod.GET, value = "/getRowsCount") + public ResponseEntity> getCountOfRows() { + HashMap map = new HashMap<>(); + map.put("lastRow", accountRepository.count()); + return ResponseEntity.ok(map); + } +} diff --git a/src/main/java/biz/nynja/account/grid/ag/AgGridService.java b/src/main/java/biz/nynja/account/grid/ag/AgGridService.java new file mode 100644 index 0000000000000000000000000000000000000000..19134273e8fe8548fcdf1525b284760d8c355562 --- /dev/null +++ b/src/main/java/biz/nynja/account/grid/ag/AgGridService.java @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.grid.ag; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.cassandra.core.query.CassandraPageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Service; + +import biz.nynja.account.admin.grpc.AccountAdminResponse; +import biz.nynja.account.admin.grpc.AccountsAdminResponse; +import biz.nynja.account.grpc.AccountDetails; +import biz.nynja.account.models.Account; +import biz.nynja.account.repositories.AccountRepository; + +@Service +public class AgGridService { + + private AccountRepository accountRepository; + + private Slice accounts = null; + + @Autowired + public AgGridService(AccountRepository accountRepository) { + this.accountRepository = accountRepository; + } + + public AccountsAdminResponse getData(int endRow, int startRow) { + + Map> pivotValues = new HashMap>(); + + // Sort sort = new Sort(new Sort.Order(Direction.ASC, "type")); + + Pageable pageable = CassandraPageRequest.of(0, endRow); + + accounts = accountRepository.findAll(pageable); + List rows = new ArrayList<>(); + accounts.getContent().subList(startRow - 1, accounts.getNumberOfElements()).forEach(account -> { + AccountDetails accountDetails = account.toProto(); + rows.add(accountDetails); + }); + + // create response with our results + + AccountsAdminResponse response = AccountsAdminResponse.newBuilder() + .setAccountsResponse(AccountAdminResponse.newBuilder().addAllAccountDetails(rows).build()).build(); + return response; + } + +} diff --git a/src/main/java/biz/nynja/account/grid/ag/GetRowsResponse.java b/src/main/java/biz/nynja/account/grid/ag/GetRowsResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..164390213ee4509d6420d0a319df666d664d9c3d --- /dev/null +++ b/src/main/java/biz/nynja/account/grid/ag/GetRowsResponse.java @@ -0,0 +1,44 @@ +package biz.nynja.account.grid.ag; + +import java.util.List; +import java.util.Map; + +public class GetRowsResponse { + + private List> data; + private int lastRow; + private List secondaryColumnFields; + + public GetRowsResponse() { + } + + public GetRowsResponse(List> data, int lastRow, List secondaryColumnFields) { + this.data = data; + this.lastRow = lastRow; + this.secondaryColumnFields = secondaryColumnFields; + } + + public List> getData() { + return data; + } + + public void setData(List> data) { + this.data = data; + } + + public int getLastRow() { + return lastRow; + } + + public void setLastRow(int lastRow) { + this.lastRow = lastRow; + } + + public List getSecondaryColumnFields() { + return secondaryColumnFields; + } + + public void setSecondaryColumns(List secondaryColumnFields) { + this.secondaryColumnFields = secondaryColumnFields; + } +} diff --git a/src/main/java/biz/nynja/account/grid/ag/ResponseBuilder.java b/src/main/java/biz/nynja/account/grid/ag/ResponseBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..4191f03eb77041854c1d7dc4e7a8696fa86b5dc6 --- /dev/null +++ b/src/main/java/biz/nynja/account/grid/ag/ResponseBuilder.java @@ -0,0 +1,56 @@ +package biz.nynja.account.grid.ag; + +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toCollection; +import static java.util.stream.Collectors.toList; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.tuple.Pair; + +import com.google.common.collect.Sets; + +import biz.nynja.account.grid.ag.request.ColumnVO; + +public class ResponseBuilder { + public static GetRowsResponse createResponse(int endRow, int startRow, + List> rows, + Map> pivotValues) { + + int currentLastRow = startRow + rows.size(); + int lastRow = currentLastRow <= endRow ? currentLastRow : -1; + + List valueColumns = new ArrayList<>(); + + return new GetRowsResponse(rows, lastRow, getSecondaryColumns(pivotValues, valueColumns)); + } + + private static List getSecondaryColumns(Map> pivotValues, List valueColumns) { + + // create pairs of pivot col and pivot value i.e. (DEALTYPE,Financial), (BIDTYPE,Sell)... + List>> pivotPairs = pivotValues.entrySet().stream() + .map(e -> e.getValue().stream() + .map(pivotValue -> Pair.of(e.getKey(), pivotValue)) + .collect(toCollection(LinkedHashSet::new))) + .collect(toList()); + + // create cartesian product of pivot and value columns i.e. Financial_Sell_CURRENTVALUE, Physical_Buy_CURRENTVALUE... + return Sets.cartesianProduct(pivotPairs) + .stream() + .flatMap(pairs -> { + // collect pivot cols, i.e. Financial_Sell + String pivotCol = pairs.stream() + .map(Pair::getRight) + .collect(joining("_")); + + // append value cols, i.e. Financial_Sell_CURRENTVALUE, Financial_Sell_PREVIOUSVALUE + return valueColumns.stream() + .map(valueCol -> pivotCol + "_" + valueCol.getField()); + }) + .collect(toList()); + } +} diff --git a/src/main/java/biz/nynja/account/grid/ag/filter/ColumnFilter.java b/src/main/java/biz/nynja/account/grid/ag/filter/ColumnFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..2dfc17f47bcb49523b04c81b89965487202823bd --- /dev/null +++ b/src/main/java/biz/nynja/account/grid/ag/filter/ColumnFilter.java @@ -0,0 +1,11 @@ +package biz.nynja.account.grid.ag.filter; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "filterType") +@JsonSubTypes({ @JsonSubTypes.Type(value = NumberColumnFilter.class, name = "number"), + @JsonSubTypes.Type(value = SetColumnFilter.class, name = "set") }) +public abstract class ColumnFilter { + String filterType; +} diff --git a/src/main/java/biz/nynja/account/grid/ag/filter/NumberColumnFilter.java b/src/main/java/biz/nynja/account/grid/ag/filter/NumberColumnFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..f7ddb55c44962f8c197d0938186cb72554c556e7 --- /dev/null +++ b/src/main/java/biz/nynja/account/grid/ag/filter/NumberColumnFilter.java @@ -0,0 +1,33 @@ +package biz.nynja.account.grid.ag.filter; + +public class NumberColumnFilter extends ColumnFilter { + + private String type; + private Integer filter; + private Integer filterTo; + + public NumberColumnFilter() { + } + + public NumberColumnFilter(String type, Integer filter, Integer filterTo) { + this.type = type; + this.filter = filter; + this.filterTo = filterTo; + } + + public String getFilterType() { + return filterType; + } + + public String getType() { + return type; + } + + public Integer getFilter() { + return filter; + } + + public Integer getFilterTo() { + return filterTo; + } +} diff --git a/src/main/java/biz/nynja/account/grid/ag/filter/SetColumnFilter.java b/src/main/java/biz/nynja/account/grid/ag/filter/SetColumnFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..f8cef7450376284a19c75072ded01f9fc0c5f8c5 --- /dev/null +++ b/src/main/java/biz/nynja/account/grid/ag/filter/SetColumnFilter.java @@ -0,0 +1,18 @@ +package biz.nynja.account.grid.ag.filter; + +import java.util.List; + +public class SetColumnFilter extends ColumnFilter { + private List values; + + public SetColumnFilter() { + } + + public SetColumnFilter(List values) { + this.values = values; + } + + public List getValues() { + return values; + } +} diff --git a/src/main/java/biz/nynja/account/grid/ag/request/ColumnVO.java b/src/main/java/biz/nynja/account/grid/ag/request/ColumnVO.java new file mode 100644 index 0000000000000000000000000000000000000000..79f1ea36da24a6e48bb3cddfbd7673cbabff1386 --- /dev/null +++ b/src/main/java/biz/nynja/account/grid/ag/request/ColumnVO.java @@ -0,0 +1,69 @@ +package biz.nynja.account.grid.ag.request; + +import java.util.Objects; + +public class ColumnVO { + + private String id; + private String displayName; + private String field; + private String aggFunc; + + public ColumnVO() { + } + + public ColumnVO(String id, String displayName, String field, String aggFunc) { + this.id = id; + this.displayName = displayName; + this.field = field; + this.aggFunc = aggFunc; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } + + public String getAggFunc() { + return aggFunc; + } + + public void setAggFunc(String aggFunc) { + this.aggFunc = aggFunc; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ColumnVO columnVO = (ColumnVO) o; + return Objects.equals(id, columnVO.id) && Objects.equals(displayName, columnVO.displayName) + && Objects.equals(field, columnVO.field) && Objects.equals(aggFunc, columnVO.aggFunc); + } + + @Override + public int hashCode() { + return Objects.hash(id, displayName, field, aggFunc); + } +} diff --git a/src/main/java/biz/nynja/account/grid/ag/request/FilterRequest.java b/src/main/java/biz/nynja/account/grid/ag/request/FilterRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..e87d451ff2694ed1761a675e09482ec2d7e40e1b --- /dev/null +++ b/src/main/java/biz/nynja/account/grid/ag/request/FilterRequest.java @@ -0,0 +1,30 @@ +package biz.nynja.account.grid.ag.request; + +import java.util.Map; + +import biz.nynja.account.grid.ag.filter.ColumnFilter; + +public class FilterRequest { + + private Map filterModel; + + public FilterRequest() { + } + + public FilterRequest(Map filterModel) { + this.filterModel = filterModel; + } + + public Map getFilterModel() { + return filterModel; + } + + public void setFilterModel(Map filterModel) { + this.filterModel = filterModel; + } + + @Override + public String toString() { + return "FilterRequest{" + "filterModel=" + filterModel + '}'; + } +} diff --git a/src/main/java/biz/nynja/account/grid/ag/request/GetRowsRequest.java b/src/main/java/biz/nynja/account/grid/ag/request/GetRowsRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..c2328878055212aee776133029b8f79be0455764 --- /dev/null +++ b/src/main/java/biz/nynja/account/grid/ag/request/GetRowsRequest.java @@ -0,0 +1,115 @@ +package biz.nynja.account.grid.ag.request; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import biz.nynja.account.admin.grpc.SortModel; +import biz.nynja.account.grid.ag.filter.ColumnFilter; + +public class GetRowsRequest { + + private int startRow, endRow; + + // row group columns + private List rowGroupCols; + + // value columns + private List valueCols; + + // pivot columns + private List pivotCols; + + // true if pivot mode is one, otherwise false + private boolean pivotMode; + + // what groups the user is viewing + private List groupKeys; + + // if filtering, what the filter model is + private Map filterModel; + + // if sorting, what the sort model is + private List sortModel; + + public GetRowsRequest() { + this.rowGroupCols = Collections.emptyList(); + this.valueCols = Collections.emptyList(); + this.pivotCols = Collections.emptyList(); + this.groupKeys = Collections.emptyList(); + this.filterModel = Collections.emptyMap(); + this.sortModel = Collections.emptyList(); + } + + public int getStartRow() { + return startRow; + } + + public void setStartRow(int startRow) { + this.startRow = startRow; + } + + public int getEndRow() { + return endRow; + } + + public void setEndRow(int endRow) { + this.endRow = endRow; + } + + public List getRowGroupCols() { + return rowGroupCols; + } + + public void setRowGroupCols(List rowGroupCols) { + this.rowGroupCols = rowGroupCols; + } + + public List getValueCols() { + return valueCols; + } + + public void setValueCols(List valueCols) { + this.valueCols = valueCols; + } + + public List getPivotCols() { + return pivotCols; + } + + public void setPivotCols(List pivotCols) { + this.pivotCols = pivotCols; + } + + public boolean isPivotMode() { + return pivotMode; + } + + public void setPivotMode(boolean pivotMode) { + this.pivotMode = pivotMode; + } + + public List getGroupKeys() { + return groupKeys; + } + + public void setGroupKeys(List groupKeys) { + this.groupKeys = groupKeys; + } + + public Map getFilterModel() { + return filterModel; + } + + public void setFilterModel(Map filterModel) { + this.filterModel = filterModel; + } + + public List getSortModel() { + return sortModel; + } + + public void setSortModel(List sortModel) { + this.sortModel = sortModel; + } +} diff --git a/src/main/java/biz/nynja/account/grpc/models/AccountInfo.java b/src/main/java/biz/nynja/account/grpc/models/AccountInfo.java deleted file mode 100644 index b87e2696f80b86ce01c8974eb8156a958a0b7c56..0000000000000000000000000000000000000000 --- a/src/main/java/biz/nynja/account/grpc/models/AccountInfo.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (C) 2018 Nynja Inc. All rights reserved. - */ -package biz.nynja.account.grpc.models; - -import org.springframework.data.cassandra.core.mapping.PrimaryKey; -import org.springframework.data.cassandra.core.mapping.Table; - -import biz.nynja.blueprint.grpc.RegisterRequest; - -/** - * Account bean as represented in the corresponding Cassandra table. - */ -@Table -public class AccountInfo { - - @PrimaryKey - private Long id; - private String email; - private String password; - - public AccountInfo() { - } - - public AccountInfo(long id, String email, String password) { - this.id = id; - this.email = email; - this.password = password; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - @Override - public String toString() { - return "AccountInfo [id=" + id + ", email=" + email + ", password=" + password + "]"; - } - - public static AccountInfo fromProto(RegisterRequest proto) { - AccountInfo accountInfo = new AccountInfo(); - accountInfo.setId(proto.getId()); - accountInfo.setEmail(proto.getEmail()); - accountInfo.setPassword(proto.getPassword()); - return accountInfo; - } - - public biz.nynja.blueprint.grpc.AccountInfo toProto() { - return biz.nynja.blueprint.grpc.AccountInfo.newBuilder().setId(Long.toString(getId())).setEmail(getEmail()) - .build(); - } -} diff --git a/src/main/java/biz/nynja/account/grpc/repositories/AccountInfoRepository.java b/src/main/java/biz/nynja/account/grpc/repositories/AccountInfoRepository.java deleted file mode 100644 index 5fc95377067b40da2f5b36e7c93fe725fbf5b326..0000000000000000000000000000000000000000 --- a/src/main/java/biz/nynja/account/grpc/repositories/AccountInfoRepository.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2018 Nynja Inc. All rights reserved. - */ -package biz.nynja.account.grpc.repositories; - -import org.springframework.data.cassandra.repository.Query; -import org.springframework.data.repository.CrudRepository; - -import biz.nynja.account.grpc.models.AccountInfo; - -/** - * Account repository for Cassandra operations/queries. - */ -public interface AccountInfoRepository extends CrudRepository { - - @Query(value = "SELECT * FROM AccountInfo WHERE email=?0 ALLOW FILTERING") - public AccountInfo findByEmail(String email); -} diff --git a/src/main/java/biz/nynja/account/grpc/services/AccountServiceImpl.java b/src/main/java/biz/nynja/account/grpc/services/AccountServiceImpl.java deleted file mode 100644 index 27414ef60ccecaea8654b3ed57b94fa52242bf8d..0000000000000000000000000000000000000000 --- a/src/main/java/biz/nynja/account/grpc/services/AccountServiceImpl.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (C) 2018 Nynja Inc. All rights reserved. - */ -package biz.nynja.account.grpc.services; - -import org.lognet.springboot.grpc.GRpcService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - -import biz.nynja.account.grpc.models.AccountInfo; -import biz.nynja.account.grpc.repositories.AccountInfoRepository; -import biz.nynja.blueprint.grpc.AccountServiceGrpc; -import biz.nynja.blueprint.grpc.RegisterError; -import biz.nynja.blueprint.grpc.RegisterError.Cause; -import biz.nynja.blueprint.grpc.RegisterRequest; -import biz.nynja.blueprint.grpc.RegisterResponse; -import io.grpc.stub.StreamObserver; - -/** - * gRPC Account service implementation.
- * The service extends the protobuf generated class and overrides the needed methods. It also saves/retrieves the - * account information via {@link AccountInfoRepository}. - */ -@GRpcService -public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBase { - - private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class); - - @Autowired - private AccountInfoRepository accountInfoRepository; - - @Override - public void register(RegisterRequest request, StreamObserver responseObserver) { - - LOGGER.info("Got a gRPC service request: {}", request); - - AccountInfo accountInfo = AccountInfo.fromProto(request); - - if (accountInfoRepository.findByEmail(accountInfo.getEmail()) != null) { - responseObserver.onNext(RegisterResponse.newBuilder() - .setError(RegisterError.newBuilder().setCause(Cause.EMAIL_ALREADY_USED)).build()); - responseObserver.onCompleted(); - return; - } - - AccountInfo savedAccount = accountInfoRepository.save(accountInfo); - LOGGER.info("Account \"{}\" saved into the DB", savedAccount.toString()); - - RegisterResponse response = RegisterResponse.newBuilder().setAccount(savedAccount.toProto()).build(); - LOGGER.info("Response from gRPC service: \"{}\"", response); - - responseObserver.onNext(response); - responseObserver.onCompleted(); - } -} diff --git a/src/main/java/biz/nynja/account/grpc/healthindicators/GrpcServerHealthIndicator.java b/src/main/java/biz/nynja/account/healthindicators/GrpcServerHealthIndicator.java similarity index 91% rename from src/main/java/biz/nynja/account/grpc/healthindicators/GrpcServerHealthIndicator.java rename to src/main/java/biz/nynja/account/healthindicators/GrpcServerHealthIndicator.java index cf6cd2da85216c971a23846ed7d01bf3a0170c82..a7c9dcf94f77305ec57615c9e098a8eda74ac8a3 100644 --- a/src/main/java/biz/nynja/account/grpc/healthindicators/GrpcServerHealthIndicator.java +++ b/src/main/java/biz/nynja/account/healthindicators/GrpcServerHealthIndicator.java @@ -1,7 +1,7 @@ /** * Copyright (C) 2018 Nynja Inc. All rights reserved. */ -package biz.nynja.account.grpc.healthindicators; +package biz.nynja.account.healthindicators; import java.util.concurrent.ExecutionException; @@ -13,8 +13,8 @@ import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; -import biz.nynja.account.grpc.services.AccountServiceImpl; -import biz.nynja.blueprint.grpc.AccountServiceGrpc; +import biz.nynja.account.grpc.AccountServiceGrpc; +import biz.nynja.account.services.AccountServiceImpl; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.health.v1.HealthCheckRequest; diff --git a/src/main/java/biz/nynja/account/models/Account.java b/src/main/java/biz/nynja/account/models/Account.java new file mode 100644 index 0000000000000000000000000000000000000000..6920d9d8f2bb71810aedf95da5031a13ec409d71 --- /dev/null +++ b/src/main/java/biz/nynja/account/models/Account.java @@ -0,0 +1,381 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import java.nio.ByteBuffer; +import java.time.LocalDate; +import java.util.Set; +import java.util.UUID; + +import org.springframework.data.cassandra.core.mapping.PrimaryKey; +import org.springframework.data.cassandra.core.mapping.Table; + +import biz.nynja.account.grpc.AccessStatus; +import biz.nynja.account.grpc.AccountDetails; +import biz.nynja.account.grpc.AccountDetails.Builder; +import biz.nynja.account.grpc.CreatePendingAccountRequest; +import biz.nynja.account.grpc.Date; +import biz.nynja.account.grpc.Role; + +@Table +public class Account { + + @PrimaryKey + private UUID accountId; + private UUID profileId; + private String accountMark; + private String authenticationProvider; + private String authenticationProviderType; + private String firstName; + private String lastName; + private ByteBuffer avatar; + private String accountName; + private String username; + private String qrCode; + private Long creationTimestamp; + private Long lastUpdateTimestamp; + private Set contactsInfo; + private Set roles; + private String accessStatus; + private LocalDate birthday; + + public UUID getAccountId() { + return accountId; + } + + public void setAccountId(UUID accountId) { + this.accountId = accountId; + } + + public UUID getProfileId() { + return profileId; + } + + public void setProfileId(UUID profileId) { + this.profileId = profileId; + } + + public String getAccountMark() { + return accountMark; + } + + public void setAccountMark(String accountMark) { + this.accountMark = accountMark; + } + + public String getAuthenticationProvider() { + return authenticationProvider; + } + + public void setAuthenticationProvider(String authenticationProvider) { + this.authenticationProvider = authenticationProvider; + } + + public String getAuthenticationProviderType() { + return authenticationProviderType; + } + + public void setAuthenticationProviderType(String authenticationProviderType) { + this.authenticationProviderType = authenticationProviderType; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public ByteBuffer getAvatar() { + return avatar; + } + + public void setAvatar(ByteBuffer avatar) { + this.avatar = avatar; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public Set getContactsInfo() { + return contactsInfo; + } + + public void setContactsInfo(Set contactsInfo) { + this.contactsInfo = contactsInfo; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getQrCode() { + return qrCode; + } + + public void setQrCode(String qrCode) { + this.qrCode = qrCode; + } + + public Long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(Long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + public Long getLastUpdateTimestamp() { + return lastUpdateTimestamp; + } + + public void setLastUpdateTimestamp(Long lastUpdateTimestamp) { + this.lastUpdateTimestamp = lastUpdateTimestamp; + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } + + public String getAccessStatus() { + return accessStatus; + } + + public void setAccessStatus(String accessStatus) { + this.accessStatus = accessStatus; + } + + public LocalDate getBirthday() { + return birthday; + } + + public void setBirthday(LocalDate birthday) { + this.birthday = birthday; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((accessStatus == null) ? 0 : accessStatus.hashCode()); + result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); + result = prime * result + ((accountMark == null) ? 0 : accountMark.hashCode()); + result = prime * result + ((accountName == null) ? 0 : accountName.hashCode()); + result = prime * result + ((authenticationProvider == null) ? 0 : authenticationProvider.hashCode()); + result = prime * result + ((authenticationProviderType == null) ? 0 : authenticationProviderType.hashCode()); + result = prime * result + ((avatar == null) ? 0 : avatar.hashCode()); + result = prime * result + ((birthday == null) ? 0 : birthday.hashCode()); + result = prime * result + ((contactsInfo == null) ? 0 : contactsInfo.hashCode()); + result = prime * result + ((creationTimestamp == null) ? 0 : creationTimestamp.hashCode()); + result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); + result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); + result = prime * result + ((lastUpdateTimestamp == null) ? 0 : lastUpdateTimestamp.hashCode()); + result = prime * result + ((profileId == null) ? 0 : profileId.hashCode()); + result = prime * result + ((qrCode == null) ? 0 : qrCode.hashCode()); + result = prime * result + ((roles == null) ? 0 : roles.hashCode()); + result = prime * result + ((username == null) ? 0 : username.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Account other = (Account) obj; + if (accessStatus == null) { + if (other.accessStatus != null) + return false; + } else if (!accessStatus.equals(other.accessStatus)) + return false; + if (accountId == null) { + if (other.accountId != null) + return false; + } else if (!accountId.equals(other.accountId)) + return false; + if (accountMark == null) { + if (other.accountMark != null) + return false; + } else if (!accountMark.equals(other.accountMark)) + return false; + if (accountName == null) { + if (other.accountName != null) + return false; + } else if (!accountName.equals(other.accountName)) + return false; + if (authenticationProvider == null) { + if (other.authenticationProvider != null) + return false; + } else if (!authenticationProvider.equals(other.authenticationProvider)) + return false; + if (authenticationProviderType == null) { + if (other.authenticationProviderType != null) + return false; + } else if (!authenticationProviderType.equals(other.authenticationProviderType)) + return false; + if (avatar == null) { + if (other.avatar != null) + return false; + } else if (!avatar.equals(other.avatar)) + return false; + if (birthday == null) { + if (other.birthday != null) + return false; + } else if (!birthday.equals(other.birthday)) + return false; + if (contactsInfo == null) { + if (other.contactsInfo != null) + return false; + } else if (!contactsInfo.equals(other.contactsInfo)) + return false; + if (creationTimestamp == null) { + if (other.creationTimestamp != null) + return false; + } else if (!creationTimestamp.equals(other.creationTimestamp)) + return false; + if (firstName == null) { + if (other.firstName != null) + return false; + } else if (!firstName.equals(other.firstName)) + return false; + if (lastName == null) { + if (other.lastName != null) + return false; + } else if (!lastName.equals(other.lastName)) + return false; + if (lastUpdateTimestamp == null) { + if (other.lastUpdateTimestamp != null) + return false; + } else if (!lastUpdateTimestamp.equals(other.lastUpdateTimestamp)) + return false; + if (profileId == null) { + if (other.profileId != null) + return false; + } else if (!profileId.equals(other.profileId)) + return false; + if (qrCode == null) { + if (other.qrCode != null) + return false; + } else if (!qrCode.equals(other.qrCode)) + return false; + if (roles == null) { + if (other.roles != null) + return false; + } else if (!roles.equals(other.roles)) + return false; + if (username == null) { + if (other.username != null) + return false; + } else if (!username.equals(other.username)) + return false; + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Account [accountId=").append(accountId).append(", profileId=").append(profileId) + .append(", accountMark=").append(accountMark).append(", authenticationProvider=") + .append(authenticationProvider).append(", authenticationProviderType=") + .append(authenticationProviderType).append(", firstName=").append(firstName).append(", lastName=") + .append(lastName).append(", avatar=").append(avatar).append(", accountName=").append(accountName) + .append(", username=").append(username).append(", qrCode=").append(qrCode) + .append(", creationTimestamp=").append(creationTimestamp).append(", lastUpdateTimestamp=") + .append(lastUpdateTimestamp).append(", contactsInfo=").append(contactsInfo).append(", roles=") + .append(roles).append(", accessStatus=").append(accessStatus).append(", birthday=").append(birthday).append("]"); + return builder.toString(); + } + + public static Account createPendingAccountFromProto(CreatePendingAccountRequest request) { + Account account = new Account(); + account.setAuthenticationProviderType(request.getAuthenticationType().toString()); + account.setAuthenticationProvider(request.getAuthenticationProvider()); + return account; + } + + public AccountDetails toProto() { + + Builder builder = AccountDetails.newBuilder(); + + if (getAccountId() != null) { + builder.setAccountId(getAccountId().toString()); + } + if (getProfileId() != null) { + builder.setProfileId(getProfileId().toString()); + } + if (getAuthenticationProvider() != null) { + builder.setAuthenticationIdentifier(getAuthenticationProvider()); + } + if (getAuthenticationProviderType() != null) { + builder.setAuthenticationType(getAuthenticationProviderType()); + } + if (getAccountMark() != null) { + builder.setAccountMark(getAccountMark()); + } + if (getAccountName() != null) { + builder.setAccountName(getAccountName()); + } + if (getFirstName() != null) { + builder.setFirstName(getFirstName()); + } + if (getLastName() != null) { + builder.setLastName(getLastName()); + } + if (getUsername() != null) { + builder.setUsername(getUsername()); + } + if (getQrCode() != null) { + builder.setQrCode(getQrCode()); + } + if (getAvatar() != null) { + builder.setAvatar(com.google.protobuf.ByteString.copyFrom(avatar)); + } + if (getRoles() != null) { + for (String role : getRoles()) { + builder.addRoles(Role.valueOf(role)); + } + } + if (getAccessStatus() != null) { + builder.setAccessStatus(AccessStatus.valueOf(getAccessStatus())); + } + if (getContactsInfo() != null) { + for (ContactInfo c : contactsInfo) { + builder.addContactsInfo(c.toProto()); + } + } + if (getBirthday() != null) { + builder.setBirthday(Date.newBuilder().setYear(getBirthday().getYear()) + .setMonth(getBirthday().getMonthValue()).setDay(getBirthday().getDayOfMonth()).build()); + } + + return builder.build(); + + } + +} diff --git a/src/main/java/biz/nynja/account/models/AccountByAuthenticationProvider.java b/src/main/java/biz/nynja/account/models/AccountByAuthenticationProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..bdfa04f39265090c0f3d7ae95c00aa643285bd2b --- /dev/null +++ b/src/main/java/biz/nynja/account/models/AccountByAuthenticationProvider.java @@ -0,0 +1,388 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import java.nio.ByteBuffer; +import java.time.LocalDate; +import java.util.Set; +import java.util.UUID; + +import biz.nynja.account.grpc.AccessStatus; +import biz.nynja.account.grpc.AccountDetails.Builder; +import biz.nynja.account.grpc.Date; +import biz.nynja.account.grpc.Role; + +public class AccountByAuthenticationProvider { + + private UUID profileId; + private UUID accountId; + private String accountMark; + private String authenticationProvider; + private String authenticationProviderType; + private String firstName; + private String lastName; + private ByteBuffer avatar; + private String accountName; + private String username; + private Long creationTimestamp; + private Long lastUpdateTimestamp; + private Set contactsInfo; + private String qrCode; + private Set roles; + private String accessStatus; + private LocalDate birthday; + + public UUID getAccountId() { + return accountId; + } + + public void setAccountId(UUID accountId) { + this.accountId = accountId; + } + + public UUID getProfileId() { + return profileId; + } + + public void setProfileId(UUID profileId) { + this.profileId = profileId; + } + + public String getAccountMark() { + return accountMark; + } + + public void setAccountMark(String accountMark) { + this.accountMark = accountMark; + } + + public String getAuthenticationProvider() { + return authenticationProvider; + } + + public void setAuthenticationProvider(String authenticationProvider) { + this.authenticationProvider = authenticationProvider; + } + + public String getAuthenticationProviderType() { + return authenticationProviderType; + } + + public void setAuthenticationProviderType(String authenticationProviderType) { + this.authenticationProviderType = authenticationProviderType; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public ByteBuffer getAvatar() { + return avatar; + } + + public void setAvatar(ByteBuffer avatar) { + this.avatar = avatar; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public Long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(Long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + public Long getLastUpdateTimestamp() { + return lastUpdateTimestamp; + } + + public void setLastUpdateTimestamp(Long lastUpdateTimestamp) { + this.lastUpdateTimestamp = lastUpdateTimestamp; + } + + public Set getContactsInfo() { + return contactsInfo; + } + + public void setContactsInfo(Set contactsInfo) { + this.contactsInfo = contactsInfo; + } + + public String getQrCode() { + return qrCode; + } + + public void setQrCode(String qrCode) { + this.qrCode = qrCode; + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } + + public String getAccessStatus() { + return accessStatus; + } + + public void setAccessStatus(String accessStatus) { + this.accessStatus = accessStatus; + } + + public LocalDate getBirthday() { + return birthday; + } + + public void setBirthday(LocalDate birthday) { + this.birthday = birthday; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((accessStatus == null) ? 0 : accessStatus.hashCode()); + result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); + result = prime * result + ((accountMark == null) ? 0 : accountMark.hashCode()); + result = prime * result + ((accountName == null) ? 0 : accountName.hashCode()); + result = prime * result + ((authenticationProvider == null) ? 0 : authenticationProvider.hashCode()); + result = prime * result + ((authenticationProviderType == null) ? 0 : authenticationProviderType.hashCode()); + result = prime * result + ((avatar == null) ? 0 : avatar.hashCode()); + result = prime * result + ((birthday == null) ? 0 : birthday.hashCode()); + result = prime * result + ((contactsInfo == null) ? 0 : contactsInfo.hashCode()); + result = prime * result + ((creationTimestamp == null) ? 0 : creationTimestamp.hashCode()); + result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); + result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); + result = prime * result + ((lastUpdateTimestamp == null) ? 0 : lastUpdateTimestamp.hashCode()); + result = prime * result + ((profileId == null) ? 0 : profileId.hashCode()); + result = prime * result + ((qrCode == null) ? 0 : qrCode.hashCode()); + result = prime * result + ((roles == null) ? 0 : roles.hashCode()); + result = prime * result + ((username == null) ? 0 : username.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AccountByAuthenticationProvider other = (AccountByAuthenticationProvider) obj; + if (accessStatus == null) { + if (other.accessStatus != null) + return false; + } else if (!accessStatus.equals(other.accessStatus)) + return false; + if (accountId == null) { + if (other.accountId != null) + return false; + } else if (!accountId.equals(other.accountId)) + return false; + if (accountMark == null) { + if (other.accountMark != null) + return false; + } else if (!accountMark.equals(other.accountMark)) + return false; + if (accountName == null) { + if (other.accountName != null) + return false; + } else if (!accountName.equals(other.accountName)) + return false; + if (authenticationProvider == null) { + if (other.authenticationProvider != null) + return false; + } else if (!authenticationProvider.equals(other.authenticationProvider)) + return false; + if (authenticationProviderType == null) { + if (other.authenticationProviderType != null) + return false; + } else if (!authenticationProviderType.equals(other.authenticationProviderType)) + return false; + if (avatar == null) { + if (other.avatar != null) + return false; + } else if (!avatar.equals(other.avatar)) + return false; + if (birthday == null) { + if (other.birthday != null) + return false; + } else if (!birthday.equals(other.birthday)) + return false; + if (contactsInfo == null) { + if (other.contactsInfo != null) + return false; + } else if (!contactsInfo.equals(other.contactsInfo)) + return false; + if (creationTimestamp == null) { + if (other.creationTimestamp != null) + return false; + } else if (!creationTimestamp.equals(other.creationTimestamp)) + return false; + if (firstName == null) { + if (other.firstName != null) + return false; + } else if (!firstName.equals(other.firstName)) + return false; + if (lastName == null) { + if (other.lastName != null) + return false; + } else if (!lastName.equals(other.lastName)) + return false; + if (lastUpdateTimestamp == null) { + if (other.lastUpdateTimestamp != null) + return false; + } else if (!lastUpdateTimestamp.equals(other.lastUpdateTimestamp)) + return false; + if (profileId == null) { + if (other.profileId != null) + return false; + } else if (!profileId.equals(other.profileId)) + return false; + if (qrCode == null) { + if (other.qrCode != null) + return false; + } else if (!qrCode.equals(other.qrCode)) + return false; + if (roles == null) { + if (other.roles != null) + return false; + } else if (!roles.equals(other.roles)) + return false; + if (username == null) { + if (other.username != null) + return false; + } else if (!username.equals(other.username)) + return false; + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("AccountByAuthenticationProvider [profileId=").append(profileId).append(", accountId=") + .append(accountId).append(", accountMark=").append(accountMark).append(", authenticationProvider=") + .append(authenticationProvider).append(", authenticationProviderType=") + .append(authenticationProviderType).append(", firstName=").append(firstName).append(", lastName=") + .append(lastName).append(", avatar=").append(avatar).append(", accountName=").append(accountName) + .append(", username=").append(username).append(", creationTimestamp=").append(creationTimestamp) + .append(", lastUpdateTimestamp=").append(lastUpdateTimestamp).append(", contactsInfo=") + .append(contactsInfo).append(", qrCode=").append(qrCode).append(", roles=").append(roles) + .append(", accessStatus=").append(accessStatus).append(", birthday=").append(birthday).append("]"); + return builder.toString(); + } + + public biz.nynja.account.grpc.AccountDetails toProto() { + + Builder builder = biz.nynja.account.grpc.AccountDetails.newBuilder(); + if (getAccountId() != null) { + builder.setAccountId(getAccountId().toString()); + } + if (getProfileId() != null) { + builder.setProfileId(getProfileId().toString()); + } + if (getAuthenticationProvider() != null) { + builder.setAuthenticationIdentifier(getAuthenticationProvider()); + } + if (getAuthenticationProviderType() != null) { + builder.setAuthenticationType(getAuthenticationProviderType()); + } + if (getAccountMark() != null) { + builder.setAccountMark(getAccountMark()); + } + if (getAccountName() != null) { + builder.setAccountName(getAccountName()); + } + if (getFirstName() != null) { + builder.setFirstName(getFirstName()); + } + if (getLastName() != null) { + builder.setLastName(getLastName()); + } + if (getUsername() != null) { + builder.setUsername(getUsername()); + } + if (getQrCode() != null) { + builder.setQrCode(getQrCode()); + } + if (avatar != null) { + builder.setAvatar(com.google.protobuf.ByteString.copyFrom(avatar)); + } + if (contactsInfo != null) { + for (ContactInfo c : contactsInfo) { + builder.addContactsInfo(c.toProto()); + } + } + if (getRoles() != null) { + for (String role : getRoles()) { + builder.addRoles(Role.valueOf(role)); + } + } + if (getAccessStatus() != null) { + builder.setAccessStatus(AccessStatus.valueOf(getAccessStatus())); + } + if (getBirthday() != null) { + builder.setBirthday(Date.newBuilder().setYear(getBirthday().getYear()) + .setMonth(getBirthday().getMonthValue()).setDay(getBirthday().getDayOfMonth()).build()); + } + + return builder.build(); + + } + + public Account toAccount() { + Account account = new Account(); + account.setAccountId(this.accountId); + account.setProfileId(this.profileId); + account.setAccountMark(this.accountMark); + account.setAuthenticationProvider(this.authenticationProvider); + account.setAuthenticationProviderType(this.authenticationProviderType); + account.setFirstName(this.firstName); + account.setLastName(this.lastName); + account.setAvatar(this.avatar); + account.setAccountName(this.accountName); + account.setUsername(this.username); + account.setCreationTimestamp(this.creationTimestamp); + account.setLastUpdateTimestamp(this.lastUpdateTimestamp); + account.setContactsInfo(this.contactsInfo); + account.setQrCode(this.qrCode); + account.setRoles(this.roles); + account.setAccessStatus(this.accessStatus); + account.setBirthday(this.birthday); + return account; + } + +} diff --git a/src/main/java/biz/nynja/account/models/AccountByCommunicationProvider.java b/src/main/java/biz/nynja/account/models/AccountByCommunicationProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..f64d3a9c1aa55b9454a4a8e574ab47b7972254b1 --- /dev/null +++ b/src/main/java/biz/nynja/account/models/AccountByCommunicationProvider.java @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import java.nio.ByteBuffer; +import java.util.UUID; + +import org.springframework.data.cassandra.core.cql.PrimaryKeyType; +import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn; +import org.springframework.data.cassandra.core.mapping.Table; + +@Table +public class AccountByCommunicationProvider { + + @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) + private String communicationProvider; + @PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED) + private String communicationProviderType; + @PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED) + private UUID accountId; + private String accountName; + private String firstName; + private ByteBuffer avatar; + + public String getCommunicationProvider() { + return communicationProvider; + } + + public void setCommunicationProvider(String communicationProvider) { + this.communicationProvider = communicationProvider; + } + + public String getCommunicationProviderType() { + return communicationProviderType; + } + + public void setCommunicationProviderType(String communicationProviderType) { + this.communicationProviderType = communicationProviderType; + } + + public UUID getAccountId() { + return accountId; + } + + public void setAccountId(UUID accountId) { + this.accountId = accountId; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public ByteBuffer getAvatar() { + return avatar; + } + + public void setAvatar(ByteBuffer avatar) { + this.avatar = avatar; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); + result = prime * result + ((accountName == null) ? 0 : accountName.hashCode()); + result = prime * result + ((avatar == null) ? 0 : avatar.hashCode()); + result = prime * result + ((communicationProvider == null) ? 0 : communicationProvider.hashCode()); + result = prime * result + ((communicationProviderType == null) ? 0 : communicationProviderType.hashCode()); + result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AccountByCommunicationProvider other = (AccountByCommunicationProvider) obj; + if (accountId == null) { + if (other.accountId != null) + return false; + } else if (!accountId.equals(other.accountId)) + return false; + if (accountName == null) { + if (other.accountName != null) + return false; + } else if (!accountName.equals(other.accountName)) + return false; + if (avatar == null) { + if (other.avatar != null) + return false; + } else if (!avatar.equals(other.avatar)) + return false; + if (communicationProvider == null) { + if (other.communicationProvider != null) + return false; + } else if (!communicationProvider.equals(other.communicationProvider)) + return false; + if (communicationProviderType == null) { + if (other.communicationProviderType != null) + return false; + } else if (!communicationProviderType.equals(other.communicationProviderType)) + return false; + if (firstName == null) { + if (other.firstName != null) + return false; + } else if (!firstName.equals(other.firstName)) + return false; + return true; + } + + @Override + public String toString() { + return new StringBuilder("AccountsByCommunicationProvider [communicationProvider=") + .append(communicationProvider).append(", communicationProviderType=").append(communicationProviderType) + .append(", accountId=").append(accountId).append(", accountName=").append(accountName) + .append(", firstName=").append(firstName).append(", avatar=").append(avatar).append("]").toString(); + } + +} diff --git a/src/main/java/biz/nynja/account/models/AccountByProfileId.java b/src/main/java/biz/nynja/account/models/AccountByProfileId.java new file mode 100644 index 0000000000000000000000000000000000000000..fe37f192bae897b104380175bfcc66cecc0d36c0 --- /dev/null +++ b/src/main/java/biz/nynja/account/models/AccountByProfileId.java @@ -0,0 +1,368 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import java.nio.ByteBuffer; +import java.time.LocalDate; +import java.util.Set; +import java.util.UUID; + +import biz.nynja.account.grpc.AccessStatus; +import biz.nynja.account.grpc.AccountDetails; +import biz.nynja.account.grpc.Date; +import biz.nynja.account.grpc.AccountDetails.Builder; +import biz.nynja.account.grpc.Role; + +public class AccountByProfileId { + + private UUID profileId; + private UUID accountId; + private String accountMark; + private String authenticationProvider; + private String authenticationProviderType; + private String firstName; + private String lastName; + private ByteBuffer avatar; + private String accountName; + private String username; + private Long creationTimestamp; + private Long lastUpdateTimestamp; + private Set contactsInfo; + private String qrCode; + private Set roles; + private String accessStatus; + private LocalDate birthday; + + public UUID getAccountId() { + return accountId; + } + + public void setAccountId(UUID accountId) { + this.accountId = accountId; + } + + public UUID getProfileId() { + return profileId; + } + + public void setProfileId(UUID profileId) { + this.profileId = profileId; + } + + public String getAccountMark() { + return accountMark; + } + + public void setAccountMark(String accountMark) { + this.accountMark = accountMark; + } + + public String getAuthenticationProvider() { + return authenticationProvider; + } + + public void setAuthenticationProvider(String authenticationProvider) { + this.authenticationProvider = authenticationProvider; + } + + public String getAuthenticationProviderType() { + return authenticationProviderType; + } + + public void setAuthenticationProviderType(String authenticationProviderType) { + this.authenticationProviderType = authenticationProviderType; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public ByteBuffer getAvatar() { + return avatar; + } + + public void setAvatar(ByteBuffer avatar) { + this.avatar = avatar; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public Long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(Long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + public Long getLastUpdateTimestamp() { + return lastUpdateTimestamp; + } + + public void setLastUpdateTimestamp(Long lastUpdateTimestamp) { + this.lastUpdateTimestamp = lastUpdateTimestamp; + } + + public Set getContactsInfo() { + return contactsInfo; + } + + public void setContactsInfo(Set contactsInfo) { + this.contactsInfo = contactsInfo; + } + + public String getQrCode() { + return qrCode; + } + + public void setQrCode(String qrCode) { + this.qrCode = qrCode; + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } + + public String getAccessStatus() { + return accessStatus; + } + + public void setAccessStatus(String accessStatus) { + this.accessStatus = accessStatus; + } + + public LocalDate getBirthday() { + return birthday; + } + + public void setBirthday(LocalDate birthday) { + this.birthday = birthday; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((accessStatus == null) ? 0 : accessStatus.hashCode()); + result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); + result = prime * result + ((accountMark == null) ? 0 : accountMark.hashCode()); + result = prime * result + ((accountName == null) ? 0 : accountName.hashCode()); + result = prime * result + ((authenticationProvider == null) ? 0 : authenticationProvider.hashCode()); + result = prime * result + ((authenticationProviderType == null) ? 0 : authenticationProviderType.hashCode()); + result = prime * result + ((avatar == null) ? 0 : avatar.hashCode()); + result = prime * result + ((birthday == null) ? 0 : birthday.hashCode()); + result = prime * result + ((contactsInfo == null) ? 0 : contactsInfo.hashCode()); + result = prime * result + ((creationTimestamp == null) ? 0 : creationTimestamp.hashCode()); + result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); + result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); + result = prime * result + ((lastUpdateTimestamp == null) ? 0 : lastUpdateTimestamp.hashCode()); + result = prime * result + ((profileId == null) ? 0 : profileId.hashCode()); + result = prime * result + ((qrCode == null) ? 0 : qrCode.hashCode()); + result = prime * result + ((roles == null) ? 0 : roles.hashCode()); + result = prime * result + ((username == null) ? 0 : username.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AccountByProfileId other = (AccountByProfileId) obj; + if (accessStatus == null) { + if (other.accessStatus != null) + return false; + } else if (!accessStatus.equals(other.accessStatus)) + return false; + if (accountId == null) { + if (other.accountId != null) + return false; + } else if (!accountId.equals(other.accountId)) + return false; + if (accountMark == null) { + if (other.accountMark != null) + return false; + } else if (!accountMark.equals(other.accountMark)) + return false; + if (accountName == null) { + if (other.accountName != null) + return false; + } else if (!accountName.equals(other.accountName)) + return false; + if (authenticationProvider == null) { + if (other.authenticationProvider != null) + return false; + } else if (!authenticationProvider.equals(other.authenticationProvider)) + return false; + if (authenticationProviderType == null) { + if (other.authenticationProviderType != null) + return false; + } else if (!authenticationProviderType.equals(other.authenticationProviderType)) + return false; + if (avatar == null) { + if (other.avatar != null) + return false; + } else if (!avatar.equals(other.avatar)) + return false; + if (birthday == null) { + if (other.birthday != null) + return false; + } else if (!birthday.equals(other.birthday)) + return false; + if (contactsInfo == null) { + if (other.contactsInfo != null) + return false; + } else if (!contactsInfo.equals(other.contactsInfo)) + return false; + if (creationTimestamp == null) { + if (other.creationTimestamp != null) + return false; + } else if (!creationTimestamp.equals(other.creationTimestamp)) + return false; + if (firstName == null) { + if (other.firstName != null) + return false; + } else if (!firstName.equals(other.firstName)) + return false; + if (lastName == null) { + if (other.lastName != null) + return false; + } else if (!lastName.equals(other.lastName)) + return false; + if (lastUpdateTimestamp == null) { + if (other.lastUpdateTimestamp != null) + return false; + } else if (!lastUpdateTimestamp.equals(other.lastUpdateTimestamp)) + return false; + if (profileId == null) { + if (other.profileId != null) + return false; + } else if (!profileId.equals(other.profileId)) + return false; + if (qrCode == null) { + if (other.qrCode != null) + return false; + } else if (!qrCode.equals(other.qrCode)) + return false; + if (roles == null) { + if (other.roles != null) + return false; + } else if (!roles.equals(other.roles)) + return false; + if (username == null) { + if (other.username != null) + return false; + } else if (!username.equals(other.username)) + return false; + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("AccountByProfileId [profileId=").append(profileId).append(", accountId=").append(accountId) + .append(", accountMark=").append(accountMark).append(", authenticationProvider=") + .append(authenticationProvider).append(", authenticationProviderType=") + .append(authenticationProviderType).append(", firstName=").append(firstName).append(", lastName=") + .append(lastName).append(", avatar=").append(avatar).append(", accountName=").append(accountName) + .append(", username=").append(username).append(", creationTimestamp=").append(creationTimestamp) + .append(", lastUpdateTimestamp=").append(lastUpdateTimestamp).append(", contactsInfo=") + .append(contactsInfo).append(", qrCode=").append(qrCode).append(", roles=").append(roles) + .append(", accessStatus=").append(accessStatus).append(", birthday=").append(birthday).append("]"); + return builder.toString(); + } + + public biz.nynja.account.grpc.AccountDetails toProto() { + + Builder builder = AccountDetails.newBuilder(); + + if (getAccountId() != null) { + builder.setAccountId(getAccountId().toString()); + } + if (getProfileId() != null) { + builder.setProfileId(getProfileId().toString()); + } + if (getAuthenticationProvider() != null) { + builder.setAuthenticationIdentifier(getAuthenticationProvider()); + } + if (getAuthenticationProviderType() != null) { + builder.setAuthenticationType(getAuthenticationProviderType()); + } + if (getAccountMark() != null) { + builder.setAccountMark(getAccountMark()); + } + if (getAccountName() != null) { + builder.setAccountName(getAccountName()); + } + if (getFirstName() != null) { + builder.setFirstName(getFirstName()); + } + if (getLastName() != null) { + builder.setLastName(getLastName()); + } + if (getUsername() != null) { + builder.setUsername(getUsername()); + } + if (getQrCode() != null) { + builder.setQrCode(getQrCode()); + } + if (getAvatar() != null) { + builder.setAvatar(com.google.protobuf.ByteString.copyFrom(avatar)); + } + if (getContactsInfo() != null) { + for (ContactInfo c : contactsInfo) { + builder.addContactsInfo(c.toProto()); + } + } + if (getRoles() != null) { + for (String role : getRoles()) { + builder.addRoles(Role.valueOf(role)); + } + } + if (getAccessStatus() != null) { + builder.setAccessStatus(AccessStatus.valueOf(getAccessStatus())); + } + if (getBirthday() != null) { + builder.setBirthday(Date.newBuilder().setYear(getBirthday().getYear()) + .setMonth(getBirthday().getMonthValue()).setDay(getBirthday().getDayOfMonth()).build()); + } + + return builder.build(); + + } + +} diff --git a/src/main/java/biz/nynja/account/models/AccountByQrCode.java b/src/main/java/biz/nynja/account/models/AccountByQrCode.java new file mode 100644 index 0000000000000000000000000000000000000000..f80d84f263caaed54a51c501be434a515a3972a2 --- /dev/null +++ b/src/main/java/biz/nynja/account/models/AccountByQrCode.java @@ -0,0 +1,345 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import java.nio.ByteBuffer; +import java.util.Set; +import java.util.UUID; + +import biz.nynja.account.grpc.AccessStatus; +import biz.nynja.account.grpc.AccountDetails; +import biz.nynja.account.grpc.Role; +import biz.nynja.account.grpc.AccountDetails.Builder; + +public class AccountByQrCode { + + private UUID profileId; + private UUID accountId; + private String accountMark; + private String authenticationProvider; + private String authenticationProviderType; + private String firstName; + private String lastName; + private ByteBuffer avatar; + private String accountName; + private String username; + private Long creationTimestamp; + private Long lastUpdateTimestamp; + private Set contactsInfo; + private String qrCode; + private String accessStatus; + private Set roles; + + public UUID getProfileId() { + return profileId; + } + + public void setProfileId(UUID profileId) { + this.profileId = profileId; + } + + public UUID getAccountId() { + return accountId; + } + + public void setAccountId(UUID accountId) { + this.accountId = accountId; + } + + public String getAccountMark() { + return accountMark; + } + + public void setAccountMark(String accountMark) { + this.accountMark = accountMark; + } + + public String getAuthenticationProvider() { + return authenticationProvider; + } + + public void setAuthenticationProvider(String authenticationProvider) { + this.authenticationProvider = authenticationProvider; + } + + public String getAuthenticationProviderType() { + return authenticationProviderType; + } + + public void setAuthenticationProviderType(String authenticationProviderType) { + this.authenticationProviderType = authenticationProviderType; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public ByteBuffer getAvatar() { + return avatar; + } + + public void setAvatar(ByteBuffer avatar) { + this.avatar = avatar; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getAccessStatus() { + return accessStatus; + } + + public void setAccessStatus(String accesstStatus) { + this.accessStatus = accesstStatus; + } + + public Long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(Long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + public Long getLastUpdateTimestamp() { + return lastUpdateTimestamp; + } + + public void setLastUpdateTimestamp(Long lastUpdateTimestamp) { + this.lastUpdateTimestamp = lastUpdateTimestamp; + } + + public Set getContactsInfo() { + return contactsInfo; + } + + public void setContactsInfo(Set contactsInfo) { + this.contactsInfo = contactsInfo; + } + + public String getQrCode() { + return qrCode; + } + + public void setQrCode(String qrCode) { + this.qrCode = qrCode; + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); + result = prime * result + ((accountMark == null) ? 0 : accountMark.hashCode()); + result = prime * result + ((accountName == null) ? 0 : accountName.hashCode()); + result = prime * result + ((accessStatus == null) ? 0 : accessStatus.hashCode()); + result = prime * result + ((authenticationProvider == null) ? 0 : authenticationProvider.hashCode()); + result = prime * result + ((authenticationProviderType == null) ? 0 : authenticationProviderType.hashCode()); + result = prime * result + ((avatar == null) ? 0 : avatar.hashCode()); + result = prime * result + ((contactsInfo == null) ? 0 : contactsInfo.hashCode()); + result = prime * result + ((creationTimestamp == null) ? 0 : creationTimestamp.hashCode()); + result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); + result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); + result = prime * result + ((lastUpdateTimestamp == null) ? 0 : lastUpdateTimestamp.hashCode()); + result = prime * result + ((profileId == null) ? 0 : profileId.hashCode()); + result = prime * result + ((qrCode == null) ? 0 : qrCode.hashCode()); + result = prime * result + ((roles == null) ? 0 : roles.hashCode()); + result = prime * result + ((username == null) ? 0 : username.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AccountByQrCode other = (AccountByQrCode) obj; + if (accountId == null) { + if (other.accountId != null) + return false; + } else if (!accountId.equals(other.accountId)) + return false; + if (accountMark == null) { + if (other.accountMark != null) + return false; + } else if (!accountMark.equals(other.accountMark)) + return false; + if (accountName == null) { + if (other.accountName != null) + return false; + } else if (!accountName.equals(other.accountName)) + return false; + if (accessStatus == null) { + if (other.accessStatus != null) + return false; + } else if (!accessStatus.equals(other.accessStatus)) + return false; + if (authenticationProvider == null) { + if (other.authenticationProvider != null) + return false; + } else if (!authenticationProvider.equals(other.authenticationProvider)) + return false; + if (authenticationProviderType == null) { + if (other.authenticationProviderType != null) + return false; + } else if (!authenticationProviderType.equals(other.authenticationProviderType)) + return false; + if (avatar == null) { + if (other.avatar != null) + return false; + } else if (!avatar.equals(other.avatar)) + return false; + if (contactsInfo == null) { + if (other.contactsInfo != null) + return false; + } else if (!contactsInfo.equals(other.contactsInfo)) + return false; + if (creationTimestamp == null) { + if (other.creationTimestamp != null) + return false; + } else if (!creationTimestamp.equals(other.creationTimestamp)) + return false; + if (firstName == null) { + if (other.firstName != null) + return false; + } else if (!firstName.equals(other.firstName)) + return false; + if (lastName == null) { + if (other.lastName != null) + return false; + } else if (!lastName.equals(other.lastName)) + return false; + if (lastUpdateTimestamp == null) { + if (other.lastUpdateTimestamp != null) + return false; + } else if (!lastUpdateTimestamp.equals(other.lastUpdateTimestamp)) + return false; + if (profileId == null) { + if (other.profileId != null) + return false; + } else if (!profileId.equals(other.profileId)) + return false; + if (qrCode == null) { + if (other.qrCode != null) + return false; + } else if (!qrCode.equals(other.qrCode)) + return false; + if (roles == null) { + if (other.roles != null) + return false; + } else if (!roles.equals(other.roles)) + return false; + if (username == null) { + if (other.username != null) + return false; + } else if (!username.equals(other.username)) + return false; + return true; + } + + @Override + public String toString() { + return new StringBuilder("AccountByQrCode [accountId=").append(accountId).append(", profileId=") + .append(profileId).append(", accountMark=").append(accountMark).append(", authenticationProvider=") + .append(authenticationProvider).append(", authenticationProviderType=") + .append(authenticationProviderType).append(", firstName=").append(firstName).append(", lastName=") + .append(lastName).append(", avatar=").append(avatar).append(", accountName=").append(accountName) + .append(", username=").append(username).append(", accessStatus=").append(accessStatus) + .append(", creationTimestamp=").append(creationTimestamp).append(", lastUpdateTimestamp=") + .append(lastUpdateTimestamp).append(", qrCode=").append(qrCode).append(", contactsInfo=") + .append(contactsInfo).append(", roles=").append(roles).append("]").toString(); + } + + public AccountDetails toProto() { + + Builder builder = AccountDetails.newBuilder(); + + if (getAccountId() != null) { + builder.setAccountId(getAccountId().toString()); + } + if (getProfileId() != null) { + builder.setProfileId(getProfileId().toString()); + } + if (getAuthenticationProvider() != null) { + builder.setAuthenticationIdentifier(getAuthenticationProvider()); + } + if (getAuthenticationProviderType() != null) { + builder.setAuthenticationType(getAuthenticationProviderType()); + } + if (getAccountMark() != null) { + builder.setAccountMark(getAccountMark()); + } + if (getAccountName() != null) { + builder.setAccountName(getAccountName()); + } + if (getFirstName() != null) { + builder.setFirstName(getFirstName()); + } + if (getLastName() != null) { + builder.setLastName(getLastName()); + } + if (getUsername() != null) { + builder.setUsername(getUsername()); + } + if (getQrCode() != null) { + builder.setQrCode(getQrCode()); + } + if (getAvatar() != null) { + builder.setAvatar(com.google.protobuf.ByteString.copyFrom(avatar)); + } + if (getRoles() != null) { + for (String role : getRoles()) { + builder.addRoles(Role.valueOf(role)); + } + } + if (getAccessStatus() != null) { + builder.setAccessStatus(AccessStatus.valueOf(getAccessStatus())); + } + if (getContactsInfo() != null) { + for (ContactInfo c : contactsInfo) { + builder.addContactsInfo(c.toProto()); + } + } + + return builder.build(); + + } + +} diff --git a/src/main/java/biz/nynja/account/models/AccountByUsername.java b/src/main/java/biz/nynja/account/models/AccountByUsername.java new file mode 100644 index 0000000000000000000000000000000000000000..ed40828a67ef508733e32e18410c77d9f4711d8a --- /dev/null +++ b/src/main/java/biz/nynja/account/models/AccountByUsername.java @@ -0,0 +1,363 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import java.nio.ByteBuffer; +import java.time.LocalDate; +import java.util.Set; +import java.util.UUID; + +import biz.nynja.account.grpc.AccessStatus; +import biz.nynja.account.grpc.AccountDetails; +import biz.nynja.account.grpc.Role; +import biz.nynja.account.grpc.AccountDetails.Builder; + +public class AccountByUsername { + + private UUID profileId; + private UUID accountId; + private String accountMark; + private String authenticationProvider; + private String authenticationProviderType; + private String firstName; + private String lastName; + private ByteBuffer avatar; + private String accountName; + private String username; + private Long creationTimestamp; + private Long lastUpdateTimestamp; + private Set contactsInfo; + private String qrCode; + private Set roles; + private String accessStatus; + private LocalDate birthday; + + public UUID getProfileId() { + return profileId; + } + + public void setProfileId(UUID profileId) { + this.profileId = profileId; + } + + public UUID getAccountId() { + return accountId; + } + + public void setAccountId(UUID accountId) { + this.accountId = accountId; + } + + public String getAccountMark() { + return accountMark; + } + + public void setAccountMark(String accountMark) { + this.accountMark = accountMark; + } + + public String getAuthenticationProvider() { + return authenticationProvider; + } + + public void setAuthenticationProvider(String authenticationProvider) { + this.authenticationProvider = authenticationProvider; + } + + public String getAuthenticationProviderType() { + return authenticationProviderType; + } + + public void setAuthenticationProviderType(String authenticationProviderType) { + this.authenticationProviderType = authenticationProviderType; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public ByteBuffer getAvatar() { + return avatar; + } + + public void setAvatar(ByteBuffer avatar) { + this.avatar = avatar; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public Long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(Long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + public Long getLastUpdateTimestamp() { + return lastUpdateTimestamp; + } + + public void setLastUpdateTimestamp(Long lastUpdateTimestamp) { + this.lastUpdateTimestamp = lastUpdateTimestamp; + } + + public Set getContactsInfo() { + return contactsInfo; + } + + public void setContactsInfo(Set contactsInfo) { + this.contactsInfo = contactsInfo; + } + + public String getQrCode() { + return qrCode; + } + + public void setQrCode(String qrCode) { + this.qrCode = qrCode; + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } + + public String getAccessStatus() { + return accessStatus; + } + + public void setAccessStatus(String accessStatus) { + this.accessStatus = accessStatus; + } + + public LocalDate getBirthday() { + return birthday; + } + + public void setBirthday(LocalDate birthday) { + this.birthday = birthday; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((accessStatus == null) ? 0 : accessStatus.hashCode()); + result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); + result = prime * result + ((accountMark == null) ? 0 : accountMark.hashCode()); + result = prime * result + ((accountName == null) ? 0 : accountName.hashCode()); + result = prime * result + ((authenticationProvider == null) ? 0 : authenticationProvider.hashCode()); + result = prime * result + ((authenticationProviderType == null) ? 0 : authenticationProviderType.hashCode()); + result = prime * result + ((avatar == null) ? 0 : avatar.hashCode()); + result = prime * result + ((birthday == null) ? 0 : birthday.hashCode()); + result = prime * result + ((contactsInfo == null) ? 0 : contactsInfo.hashCode()); + result = prime * result + ((creationTimestamp == null) ? 0 : creationTimestamp.hashCode()); + result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); + result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); + result = prime * result + ((lastUpdateTimestamp == null) ? 0 : lastUpdateTimestamp.hashCode()); + result = prime * result + ((profileId == null) ? 0 : profileId.hashCode()); + result = prime * result + ((qrCode == null) ? 0 : qrCode.hashCode()); + result = prime * result + ((roles == null) ? 0 : roles.hashCode()); + result = prime * result + ((username == null) ? 0 : username.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AccountByUsername other = (AccountByUsername) obj; + if (accessStatus == null) { + if (other.accessStatus != null) + return false; + } else if (!accessStatus.equals(other.accessStatus)) + return false; + if (accountId == null) { + if (other.accountId != null) + return false; + } else if (!accountId.equals(other.accountId)) + return false; + if (accountMark == null) { + if (other.accountMark != null) + return false; + } else if (!accountMark.equals(other.accountMark)) + return false; + if (accountName == null) { + if (other.accountName != null) + return false; + } else if (!accountName.equals(other.accountName)) + return false; + if (authenticationProvider == null) { + if (other.authenticationProvider != null) + return false; + } else if (!authenticationProvider.equals(other.authenticationProvider)) + return false; + if (authenticationProviderType == null) { + if (other.authenticationProviderType != null) + return false; + } else if (!authenticationProviderType.equals(other.authenticationProviderType)) + return false; + if (avatar == null) { + if (other.avatar != null) + return false; + } else if (!avatar.equals(other.avatar)) + return false; + if (birthday == null) { + if (other.birthday != null) + return false; + } else if (!birthday.equals(other.birthday)) + return false; + if (contactsInfo == null) { + if (other.contactsInfo != null) + return false; + } else if (!contactsInfo.equals(other.contactsInfo)) + return false; + if (creationTimestamp == null) { + if (other.creationTimestamp != null) + return false; + } else if (!creationTimestamp.equals(other.creationTimestamp)) + return false; + if (firstName == null) { + if (other.firstName != null) + return false; + } else if (!firstName.equals(other.firstName)) + return false; + if (lastName == null) { + if (other.lastName != null) + return false; + } else if (!lastName.equals(other.lastName)) + return false; + if (lastUpdateTimestamp == null) { + if (other.lastUpdateTimestamp != null) + return false; + } else if (!lastUpdateTimestamp.equals(other.lastUpdateTimestamp)) + return false; + if (profileId == null) { + if (other.profileId != null) + return false; + } else if (!profileId.equals(other.profileId)) + return false; + if (qrCode == null) { + if (other.qrCode != null) + return false; + } else if (!qrCode.equals(other.qrCode)) + return false; + if (roles == null) { + if (other.roles != null) + return false; + } else if (!roles.equals(other.roles)) + return false; + if (username == null) { + if (other.username != null) + return false; + } else if (!username.equals(other.username)) + return false; + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("AccountByUsername [profileId=").append(profileId).append(", accountId=").append(accountId) + .append(", accountMark=").append(accountMark).append(", authenticationProvider=") + .append(authenticationProvider).append(", authenticationProviderType=") + .append(authenticationProviderType).append(", firstName=").append(firstName).append(", lastName=") + .append(lastName).append(", avatar=").append(avatar).append(", accountName=").append(accountName) + .append(", username=").append(username).append(", creationTimestamp=").append(creationTimestamp) + .append(", lastUpdateTimestamp=").append(lastUpdateTimestamp).append(", contactsInfo=") + .append(contactsInfo).append(", qrCode=").append(qrCode).append(", roles=").append(roles) + .append(", accessStatus=").append(accessStatus).append(", birthday=").append(birthday).append("]"); + return builder.toString(); + } + + public AccountDetails toProto() { + + Builder builder = AccountDetails.newBuilder(); + + if (getAccountId() != null) { + builder.setAccountId(getAccountId().toString()); + } + if (getProfileId() != null) { + builder.setProfileId(getProfileId().toString()); + } + if (getAuthenticationProvider() != null) { + builder.setAuthenticationIdentifier(getAuthenticationProvider()); + } + if (getAuthenticationProviderType() != null) { + builder.setAuthenticationType(getAuthenticationProviderType()); + } + if (getAccountMark() != null) { + builder.setAccountMark(getAccountMark()); + } + if (getAccountName() != null) { + builder.setAccountName(getAccountName()); + } + if (getFirstName() != null) { + builder.setFirstName(getFirstName()); + } + if (getLastName() != null) { + builder.setLastName(getLastName()); + } + if (getUsername() != null) { + builder.setUsername(getUsername()); + } + if (getQrCode() != null) { + builder.setQrCode(getQrCode()); + } + if (getAvatar() != null) { + builder.setAvatar(com.google.protobuf.ByteString.copyFrom(avatar)); + } + if (getRoles() != null) { + for (String role : getRoles()) { + builder.addRoles(Role.valueOf(role)); + } + } + if (getAccessStatus() != null) { + builder.setAccessStatus(AccessStatus.valueOf(getAccessStatus())); + } + if (getContactsInfo() != null) { + for (ContactInfo c : contactsInfo) { + builder.addContactsInfo(c.toProto()); + } + } + + return builder.build(); + + } + +} diff --git a/src/main/java/biz/nynja/account/models/AuthenticationProvider.java b/src/main/java/biz/nynja/account/models/AuthenticationProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..3a2eace75b3ae5d392d88f2743088d15dac20a36 --- /dev/null +++ b/src/main/java/biz/nynja/account/models/AuthenticationProvider.java @@ -0,0 +1,104 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import org.springframework.data.cassandra.core.mapping.UserDefinedType; + +import biz.nynja.account.grpc.AuthProviderDetails; +import biz.nynja.account.grpc.AuthenticationType; + +@UserDefinedType +public class AuthenticationProvider { + + private String type; + private String value; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AuthenticationProvider other = (AuthenticationProvider) obj; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + + @Override + public String toString() { + return new StringBuilder("AuthenticationProvider [type=").append(type).append(", value=").append(value) + .append("]").toString(); + } + + public static AuthenticationProvider createAuthenticationProviderFromProto( + AuthProviderDetails authProviderDetails) { + AuthenticationProvider authenticationProvider = new AuthenticationProvider(); + authenticationProvider.setType(authProviderDetails.getAuthenticationType().toString()); + authenticationProvider.setValue(authProviderDetails.getAuthenticationProvider()); + return authenticationProvider; + } + + public static AuthenticationProvider createAuthenticationProviderFromStrings(String type, String value) { + AuthenticationProvider authenticationProvider = new AuthenticationProvider(); + authenticationProvider.setType(type); + authenticationProvider.setValue(value); + return authenticationProvider; + } + + public AuthProviderDetails toProto() { + AuthenticationType protoType = null; + switch (getType()) { + case "PHONE": + protoType = AuthenticationType.PHONE; + break; + case "EMAIL": + protoType = AuthenticationType.EMAIL; + break; + case "FACEBOOK": + protoType = AuthenticationType.FACEBOOK; + break; + case "GOOGLEPLUS": + protoType = AuthenticationType.GOOGLEPLUS; + break; + } + return AuthProviderDetails.newBuilder().setAuthenticationProvider(getValue()).setAuthenticationType(protoType) + .build(); + } +} diff --git a/src/main/java/biz/nynja/account/models/ContactInfo.java b/src/main/java/biz/nynja/account/models/ContactInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..9fffa48da04b0e8905defe0ab83842efd6c983c5 --- /dev/null +++ b/src/main/java/biz/nynja/account/models/ContactInfo.java @@ -0,0 +1,133 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import org.springframework.data.cassandra.core.mapping.UserDefinedType; + +import biz.nynja.account.grpc.ContactDetails; +import biz.nynja.account.grpc.ContactDetails.Builder; +import biz.nynja.account.grpc.ContactType; + +@UserDefinedType +public class ContactInfo { + + private String type; + private String value; + private String label; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((label == null) ? 0 : label.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ContactInfo other = (ContactInfo) obj; + if (label == null) { + if (other.label != null) + return false; + } else if (!label.equals(other.label)) + return false; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + + @Override + public String toString() { + return new StringBuilder("Contact info [type=").append(type).append(", value=").append(value).append(", label=") + .append(label).append("]").toString(); + } + + public static ContactInfo createContactInfoFromProto(ContactDetails contactDetails) { + ContactInfo contactInfo = new ContactInfo(); + contactInfo.setType(contactDetails.getType().toString()); + contactInfo.setValue(contactDetails.getValue()); + contactInfo.setLabel(contactDetails.getLabel()); + return contactInfo; + } + + public ContactDetails toProto() { + + Builder builder = ContactDetails.newBuilder(); + + ContactType contactType = buildContactType(getType()); + if (contactType != null) { + builder.setType(contactType); + } + if (getValue() != null) { + builder.setValue(getValue()); + } + if (getLabel() != null) { + builder.setLabel(getLabel()); + } + + return builder.build(); + } + + private ContactType buildContactType(String type) { + ContactType contactType = null; + switch (type) { + case "PHONE_CONTACT": + contactType = ContactType.PHONE_CONTACT; + break; + case "EMAIL_CONTACT": + contactType = ContactType.EMAIL_CONTACT; + break; + case "FACEBOOK_CONTACT": + contactType = ContactType.FACEBOOK_CONTACT; + break; + case "GOOGLEPLUS_CONTACT": + contactType = ContactType.GOOGLEPLUS_CONTACT; + break; + case "TWITTER_CONTACT": + contactType = ContactType.TWITTER_CONTACT; + break; + } + return contactType; + } +} diff --git a/src/main/java/biz/nynja/account/models/CountryInfo.java b/src/main/java/biz/nynja/account/models/CountryInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..8414d463cdf1703339b9450af9c3c8c419a09b44 --- /dev/null +++ b/src/main/java/biz/nynja/account/models/CountryInfo.java @@ -0,0 +1,88 @@ +package biz.nynja.account.models; + +public class CountryInfo { + + private String countryPhoneCode; + private String countryCode; + private String countryName; + private String phoneFormat; + + public CountryInfo() { + } + + public String getCountryPhoneCode() { + return countryPhoneCode; + } + + public void setCountryPhoneCode(String countryPhoneCode) { + this.countryPhoneCode = countryPhoneCode; + } + + public String getCountryCode() { + return countryCode; + } + + public void setCountryCode(String countryCode) { + this.countryCode = countryCode; + } + + public String getCountryName() { + return countryName; + } + + public void setCountryName(String countryName) { + this.countryName = countryName; + } + + public String getPhoneFormat() { + return phoneFormat; + } + + public void setPhoneFormat(String phoneFormat) { + this.phoneFormat = phoneFormat; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((countryCode == null) ? 0 : countryCode.hashCode()); + result = prime * result + ((countryName == null) ? 0 : countryName.hashCode()); + result = prime * result + ((countryPhoneCode == null) ? 0 : countryPhoneCode.hashCode()); + result = prime * result + ((phoneFormat == null) ? 0 : phoneFormat.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CountryInfo other = (CountryInfo) obj; + if (countryCode == null) { + if (other.countryCode != null) + return false; + } else if (!countryCode.equals(other.countryCode)) + return false; + if (countryName == null) { + if (other.countryName != null) + return false; + } else if (!countryName.equals(other.countryName)) + return false; + if (countryPhoneCode == null) { + if (other.countryPhoneCode != null) + return false; + } else if (!countryPhoneCode.equals(other.countryPhoneCode)) + return false; + if (phoneFormat == null) { + if (other.phoneFormat != null) + return false; + } else if (!phoneFormat.equals(other.phoneFormat)) + return false; + return true; + } + +} diff --git a/src/main/java/biz/nynja/account/models/PendingAccount.java b/src/main/java/biz/nynja/account/models/PendingAccount.java new file mode 100644 index 0000000000000000000000000000000000000000..11d77581bdc460314c7dd6d49f0e105f7dfb4baf --- /dev/null +++ b/src/main/java/biz/nynja/account/models/PendingAccount.java @@ -0,0 +1,131 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import java.util.UUID; + +import org.springframework.data.cassandra.core.mapping.PrimaryKey; +import org.springframework.data.cassandra.core.mapping.Table; + +import biz.nynja.account.grpc.CreatePendingAccountRequest; + +@Table +public class PendingAccount { + @PrimaryKey + private UUID accountId; + private UUID profileId; + private String authenticationProvider; + private String authenticationProviderType; + private Long creationTimestamp; + + public UUID getAccountId() { + return accountId; + } + + public void setAccountId(UUID accountId) { + this.accountId = accountId; + } + + public UUID getProfileId() { + return profileId; + } + + public void setProfileId(UUID profileId) { + this.profileId = profileId; + } + + public String getAuthenticationProvider() { + return authenticationProvider; + } + + public void setAuthenticationProvider(String authenticationProvider) { + this.authenticationProvider = authenticationProvider; + } + + public String getAuthenticationProviderType() { + return authenticationProviderType; + } + + public void setAuthenticationProviderType(String authenticationProviderType) { + this.authenticationProviderType = authenticationProviderType; + } + + public Long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(Long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); + result = prime * result + ((authenticationProvider == null) ? 0 : authenticationProvider.hashCode()); + result = prime * result + ((authenticationProviderType == null) ? 0 : authenticationProviderType.hashCode()); + result = prime * result + ((creationTimestamp == null) ? 0 : creationTimestamp.hashCode()); + result = prime * result + ((profileId == null) ? 0 : profileId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PendingAccount other = (PendingAccount) obj; + if (accountId == null) { + if (other.accountId != null) + return false; + } else if (!accountId.equals(other.accountId)) + return false; + if (authenticationProvider == null) { + if (other.authenticationProvider != null) + return false; + } else if (!authenticationProvider.equals(other.authenticationProvider)) + return false; + if (authenticationProviderType == null) { + if (other.authenticationProviderType != null) + return false; + } else if (!authenticationProviderType.equals(other.authenticationProviderType)) + return false; + if (creationTimestamp == null) { + if (other.creationTimestamp != null) + return false; + } else if (!creationTimestamp.equals(other.creationTimestamp)) + return false; + if (profileId == null) { + if (other.profileId != null) + return false; + } else if (!profileId.equals(other.profileId)) + return false; + return true; + } + + @Override + public String toString() { + return new StringBuilder("PendingAccounts [accountId=").append(accountId).append(", profileId=") + .append(profileId).append(", authenticationProvider=").append(authenticationProvider) + .append(", authenticationProviderType=").append(authenticationProviderType) + .append(", creationTimestamp=").append(creationTimestamp).append("]").toString(); + } + + public static PendingAccount fromProto(CreatePendingAccountRequest proto) { + PendingAccount pendingAccount = new PendingAccount(); + pendingAccount.setAuthenticationProviderType(proto.getAuthenticationType().toString()); + pendingAccount.setAuthenticationProvider(proto.getAuthenticationProvider()); + return pendingAccount; + } + + public biz.nynja.account.grpc.PendingAccountDetails toProto() { + return biz.nynja.account.grpc.PendingAccountDetails.newBuilder().setAccountId(getAccountId().toString()) + .build(); + } + +} diff --git a/src/main/java/biz/nynja/account/models/PendingAccountByAuthenticationProvider.java b/src/main/java/biz/nynja/account/models/PendingAccountByAuthenticationProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..35c07e919e4d4f9a2925926efee711a47745262b --- /dev/null +++ b/src/main/java/biz/nynja/account/models/PendingAccountByAuthenticationProvider.java @@ -0,0 +1,112 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import java.util.UUID; + +public class PendingAccountByAuthenticationProvider { + private UUID accountId; + private UUID profileId; + private String authenticationProvider; + private String authenticationProviderType; + private Long creationTimestamp; + + public UUID getAccountId() { + return accountId; + } + + public void setAccountId(UUID accountId) { + this.accountId = accountId; + } + + public UUID getProfileId() { + return profileId; + } + + public void setProfileId(UUID profileId) { + this.profileId = profileId; + } + + public String getAuthenticationProvider() { + return authenticationProvider; + } + + public void setAuthenticationProvider(String authenticationProvider) { + this.authenticationProvider = authenticationProvider; + } + + public String getAuthenticationProviderType() { + return authenticationProviderType; + } + + public void setAuthenticationProviderType(String authenticationProviderType) { + this.authenticationProviderType = authenticationProviderType; + } + + public Long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(Long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); + result = prime * result + ((authenticationProvider == null) ? 0 : authenticationProvider.hashCode()); + result = prime * result + ((authenticationProviderType == null) ? 0 : authenticationProviderType.hashCode()); + result = prime * result + ((creationTimestamp == null) ? 0 : creationTimestamp.hashCode()); + result = prime * result + ((profileId == null) ? 0 : profileId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PendingAccountByAuthenticationProvider other = (PendingAccountByAuthenticationProvider) obj; + if (accountId == null) { + if (other.accountId != null) + return false; + } else if (!accountId.equals(other.accountId)) + return false; + if (authenticationProvider == null) { + if (other.authenticationProvider != null) + return false; + } else if (!authenticationProvider.equals(other.authenticationProvider)) + return false; + if (authenticationProviderType == null) { + if (other.authenticationProviderType != null) + return false; + } else if (!authenticationProviderType.equals(other.authenticationProviderType)) + return false; + if (creationTimestamp == null) { + if (other.creationTimestamp != null) + return false; + } else if (!creationTimestamp.equals(other.creationTimestamp)) + return false; + if (profileId == null) { + if (other.profileId != null) + return false; + } else if (!profileId.equals(other.profileId)) + return false; + return true; + } + + @Override + public String toString() { + return new StringBuilder("PendingAccounts [accountId=").append(accountId).append(", profileId=") + .append(profileId).append(", authenticationProvider=").append(authenticationProvider) + .append(", authenticationProviderType=").append(authenticationProviderType) + .append(", creationTimestamp=").append(creationTimestamp).append("]").toString(); + } + +} diff --git a/src/main/java/biz/nynja/account/models/Profile.java b/src/main/java/biz/nynja/account/models/Profile.java new file mode 100644 index 0000000000000000000000000000000000000000..f4e284455b7cc37e764f4d43f1c7dd02af3f6434 --- /dev/null +++ b/src/main/java/biz/nynja/account/models/Profile.java @@ -0,0 +1,158 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import java.util.Set; +import java.util.UUID; + +import org.springframework.data.cassandra.core.mapping.PrimaryKey; +import org.springframework.data.cassandra.core.mapping.Table; +import biz.nynja.account.grpc.ProfileDetails; +import biz.nynja.account.grpc.ProfileDetails.Builder; + +@Table +public class Profile { + + @PrimaryKey + private UUID profileId; + private Set authenticationProviders; + private String passcode; + private UUID defaultAccount; + private Long creationTimestamp; + private Long lastUpdateTimestamp; + + public UUID getProfileId() { + return profileId; + } + + public void setProfileId(UUID profileId) { + this.profileId = profileId; + } + + public Set getAuthenticationProviders() { + return authenticationProviders; + } + + public void setAuthenticationProviders(Set authenticationProviders) { + this.authenticationProviders = authenticationProviders; + } + + public Long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(Long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + public Long getLastUpdateTimestamp() { + return lastUpdateTimestamp; + } + + public void setLastUpdateTimestamp(Long lastUpdateTimestamp) { + this.lastUpdateTimestamp = lastUpdateTimestamp; + } + + public String getPasscode() { + return passcode; + } + + public void setPasscode(String passcode) { + this.passcode = passcode; + } + + public UUID getDefaultAccount() { + return defaultAccount; + } + + public void setDefaultAccount(UUID defaultAccount) { + this.defaultAccount = defaultAccount; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((authenticationProviders == null) ? 0 : authenticationProviders.hashCode()); + result = prime * result + ((creationTimestamp == null) ? 0 : creationTimestamp.hashCode()); + result = prime * result + ((defaultAccount == null) ? 0 : defaultAccount.hashCode()); + result = prime * result + ((lastUpdateTimestamp == null) ? 0 : lastUpdateTimestamp.hashCode()); + result = prime * result + ((passcode == null) ? 0 : passcode.hashCode()); + result = prime * result + ((profileId == null) ? 0 : profileId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Profile other = (Profile) obj; + if (authenticationProviders == null) { + if (other.authenticationProviders != null) + return false; + } else if (!authenticationProviders.equals(other.authenticationProviders)) + return false; + if (creationTimestamp == null) { + if (other.creationTimestamp != null) + return false; + } else if (!creationTimestamp.equals(other.creationTimestamp)) + return false; + if (defaultAccount == null) { + if (other.defaultAccount != null) + return false; + } else if (!defaultAccount.equals(other.defaultAccount)) + return false; + if (lastUpdateTimestamp == null) { + if (other.lastUpdateTimestamp != null) + return false; + } else if (!lastUpdateTimestamp.equals(other.lastUpdateTimestamp)) + return false; + if (passcode == null) { + if (other.passcode != null) + return false; + } else if (!passcode.equals(other.passcode)) + return false; + if (profileId == null) { + if (other.profileId != null) + return false; + } else if (!profileId.equals(other.profileId)) + return false; + return true; + } + + @Override + public String toString() { + return new StringBuilder("Profile [profileId=").append(profileId).append(", authenticationProviders=") + .append(authenticationProviders).append(", creationTimestamp=").append(creationTimestamp) + .append(", lastUpdateTimestamp=").append(lastUpdateTimestamp).append(", passcode=******") + .append(", defaultAccount=").append(defaultAccount).append("]").toString(); + } + + public ProfileDetails toProto() { + Builder builder = ProfileDetails.newBuilder(); + if (getProfileId() != null) { + builder.setProfileId(getProfileId().toString()); + } + if (getPasscode() != null) { + builder.setPasscode(getPasscode()); + } + if (getDefaultAccount() != null) { + builder.setDefaultAccountId(getDefaultAccount().toString()); + } + if (getAuthenticationProviders() != null) { + for (AuthenticationProvider authenticationProvider : authenticationProviders) { + builder.addAuthProviders(authenticationProvider.toProto()); + } + } + return builder.build(); + } + + public boolean removeAuthenticationProvider(AuthenticationProvider ap) { + return authenticationProviders.remove(ap); + } +} diff --git a/src/main/java/biz/nynja/account/models/ProfileByAuthenticationProvider.java b/src/main/java/biz/nynja/account/models/ProfileByAuthenticationProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..cbfd96b357421e9323f1c6ef06ee7f7d6de85c38 --- /dev/null +++ b/src/main/java/biz/nynja/account/models/ProfileByAuthenticationProvider.java @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.models; + +import java.util.UUID; + +import org.springframework.data.cassandra.core.cql.PrimaryKeyType; +import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn; +import org.springframework.data.cassandra.core.mapping.Table; + +@Table +public class ProfileByAuthenticationProvider { + + @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) + private String authenticationProvider; + @PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED) + private String authenticationProviderType; + private UUID profileId; + + public String getAuthenticationProvider() { + return authenticationProvider; + } + + public void setAuthenticationProvider(String authenticationProvider) { + this.authenticationProvider = authenticationProvider; + } + + public String getAuthenticationProviderType() { + return authenticationProviderType; + } + + public void setAuthenticationProviderType(String authenticationProviderType) { + this.authenticationProviderType = authenticationProviderType; + } + + public UUID getProfileId() { + return profileId; + } + + public void setProfileId(UUID profileId) { + this.profileId = profileId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((authenticationProvider == null) ? 0 : authenticationProvider.hashCode()); + result = prime * result + ((authenticationProviderType == null) ? 0 : authenticationProviderType.hashCode()); + result = prime * result + ((profileId == null) ? 0 : profileId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ProfileByAuthenticationProvider other = (ProfileByAuthenticationProvider) obj; + if (authenticationProvider == null) { + if (other.authenticationProvider != null) + return false; + } else if (!authenticationProvider.equals(other.authenticationProvider)) + return false; + if (authenticationProviderType == null) { + if (other.authenticationProviderType != null) + return false; + } else if (!authenticationProviderType.equals(other.authenticationProviderType)) + return false; + if (profileId == null) { + if (other.profileId != null) + return false; + } else if (!profileId.equals(other.profileId)) + return false; + return true; + } + + @Override + public String toString() { + return new StringBuilder("ProfileByAuthenticationProvider [authenticationProvider=") + .append(authenticationProvider).append(", authenticationProviderType=") + .append(authenticationProviderType).append(", profileId=").append(profileId).append("]").toString(); + } + +} diff --git a/src/main/java/biz/nynja/account/phone/InvalidPhoneNumberException.java b/src/main/java/biz/nynja/account/phone/InvalidPhoneNumberException.java new file mode 100644 index 0000000000000000000000000000000000000000..51efe7344e0cadf63c62bf60b91aa8c615c40551 --- /dev/null +++ b/src/main/java/biz/nynja/account/phone/InvalidPhoneNumberException.java @@ -0,0 +1,8 @@ +package biz.nynja.account.phone; + +public class InvalidPhoneNumberException extends RuntimeException{ + + public InvalidPhoneNumberException(String msg) { + super(msg); + } +} diff --git a/src/main/java/biz/nynja/account/phone/PhoneNumberNormalizer.java b/src/main/java/biz/nynja/account/phone/PhoneNumberNormalizer.java new file mode 100644 index 0000000000000000000000000000000000000000..e9b434f1703bf22c686f21508a91a5804661b42d --- /dev/null +++ b/src/main/java/biz/nynja/account/phone/PhoneNumberNormalizer.java @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.phone; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import biz.nynja.account.components.Validator; +import biz.nynja.account.grpc.AddContactInfoRequest; +import biz.nynja.account.grpc.ContactDetails; +import biz.nynja.account.grpc.DeleteContactInfoRequest; +import biz.nynja.account.grpc.EditContactInfoRequest; + +@Service +public class PhoneNumberNormalizer { + + @Autowired + private PhoneNumberValidator validator; + + public AddContactInfoRequest normalizePhoneNumber(AddContactInfoRequest request) { + // Get the normalized phone number from libphone + ContactDetails newContactDetails = ContactDetails.newBuilder().setType(request.getContactInfo().getType()) + .setValue(validator.getNormalizedPhoneNumber(request.getContactInfo().getValue())) + .setLabel(request.getContactInfo().getLabel()).build(); + AddContactInfoRequest newRequest = AddContactInfoRequest.newBuilder().setAccountId(request.getAccountId()) + .setContactInfo(newContactDetails).build(); + request = newRequest; + return request; + } + + public DeleteContactInfoRequest normalizePhoneNumber(DeleteContactInfoRequest request) { + // Get the normalized phone number from libphone + ContactDetails newContactDetails = ContactDetails.newBuilder().setType(request.getContactInfo().getType()) + .setValue(validator.getNormalizedPhoneNumber(request.getContactInfo().getValue())) + .setLabel(request.getContactInfo().getLabel()).build(); + DeleteContactInfoRequest newRequest = DeleteContactInfoRequest.newBuilder().setAccountId(request.getAccountId()) + .setContactInfo(newContactDetails).build(); + request = newRequest; + return request; + } + + public EditContactInfoRequest normalizePhoneNumbers(EditContactInfoRequest request) { + // Get the normalized phone number from libphone for the old number + ContactDetails contactDetailsOldNumber = ContactDetails.newBuilder() + .setType(request.getOldContactInfo().getType()) + .setValue(validator.getNormalizedPhoneNumber(request.getOldContactInfo().getValue())) + .setLabel(request.getOldContactInfo().getLabel()).build(); + // Get the normalized phone number from libphone for the edited number + ContactDetails contactDetailsEditedNumber = ContactDetails.newBuilder() + .setType(request.getEditedContactInfo().getType()) + .setValue(validator.getNormalizedPhoneNumber(request.getEditedContactInfo().getValue())) + .setLabel(request.getEditedContactInfo().getLabel()).build(); + EditContactInfoRequest newRequest = EditContactInfoRequest.newBuilder().setAccountId(request.getAccountId()) + .setOldContactInfo(contactDetailsOldNumber).setEditedContactInfo(contactDetailsEditedNumber).build(); + request = newRequest; + return request; + } +} diff --git a/src/main/java/biz/nynja/account/phone/PhoneNumberValidator.java b/src/main/java/biz/nynja/account/phone/PhoneNumberValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..61b077679646697223db601a8ab85934e2b6d159 --- /dev/null +++ b/src/main/java/biz/nynja/account/phone/PhoneNumberValidator.java @@ -0,0 +1,208 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ + +package biz.nynja.account.phone; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.PostConstruct; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Component; + +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber; +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; + +import biz.nynja.account.grpc.ErrorResponse.Cause; +import biz.nynja.account.models.CountryInfo; +import biz.nynja.account.validation.ValidationError; + +@Component +public class PhoneNumberValidator { + private static final Logger logger = LoggerFactory.getLogger(PhoneNumberValidator.class); + private HashMap countryInfoMap; + private HashMap countryMapByCountryCode; + + @PostConstruct + public void loadPhonesBook() { + + CountryInfo countryInfo = null; + BufferedReader reader = null; + countryInfoMap = new HashMap<>(); + countryMapByCountryCode = new HashMap<>(); + + logger.debug("Loading phones information from file."); + try { + Resource resource = new ClassPathResource("countries.txt"); + InputStream resourceInputStream = resource.getInputStream(); + logger.debug("Phones information loaded."); + reader = new BufferedReader(new InputStreamReader(resourceInputStream)); + String line; + while ((line = reader.readLine()) != null) { + String[] args = line.split(";"); + countryInfo = new CountryInfo(); + countryInfo.setCountryCode(args[1]); + countryInfo.setCountryPhoneCode(args[0]); + countryInfo.setCountryName(args[2]); + if (args.length > 3) { + countryInfo.setPhoneFormat(args[3]); + } + countryInfoMap.put(args[1], countryInfo); + countryMapByCountryCode.put(args[0], countryInfo); + } + } catch (IOException e) { + logger.error("Error during load phones information: {}", e.getMessage()); + } finally { + try { + reader.close(); + } catch (IOException e) { + logger.error("Close reader error: {}", e.getMessage()); + } + } + + } + + public boolean isPhoneNumberValid(String phoneNumber, String countryCode) { + + final int NATIONAL_NUMBER_MAX_LENGTH = 15; + + logger.debug("Checking phoneNumber: {} for country: {}", phoneNumber, countryCode); + CountryInfo countryInfo = countryInfoMap.get(countryCode); + if (countryInfo == null) { + logger.debug("Country: {} not found!", countryCode); + return false; + } + + char[] digits = countryInfo.getCountryPhoneCode().toCharArray(); + StringBuilder builder = new StringBuilder(); + for (char digit : digits) { + builder.append("[" + digit + "]"); + } + String codePattern = builder.toString(); + + String phoneLength = null; + if (countryInfo.getPhoneFormat() != null) { + phoneLength = "{" + countryInfo.getPhoneFormat().replaceAll("\\s", "").length() + "}"; + } else { + phoneLength = "{0," + NATIONAL_NUMBER_MAX_LENGTH + "}"; + } + + final String PHONE_PATTERN = "((?:\\+?([0][0])?" + codePattern + ")?||([0][0]" + codePattern + ")?)(\\d" + + phoneLength + ")$"; + logger.debug("Generated phone pattern for country: {}, {}", countryCode, PHONE_PATTERN); + + Pattern pattern = Pattern.compile(PHONE_PATTERN); + Matcher matcher = pattern.matcher(phoneNumber); + + boolean isValid = matcher.matches(); + logger.debug("PhoneNumber: {} for country: {} is valid: {}", phoneNumber, countryCode, isValid); + + return isValid; + } + + /** + * + * @param rawPhoneNumber + * it could be in format + i.e +359878123456 or 00 i.e + * 00359878123456 or : i.e :359878225840 + */ + public boolean isPhoneNumberValid(String rawPhoneNumber) { + String phoneNumber; + + String[] provider = rawPhoneNumber.split(":"); + if (provider.length == 1) { // no country selector + phoneNumber = provider[0]; + } else { + phoneNumber = provider[1]; + } + if (!phoneNumber.matches("^\\+?[\\d- ]+$")) { + return false; + } + phoneNumber = phoneNumber.replaceAll("^0*", "").replaceAll("[^\\d]", ""); + + String countrySelector; + try { + countrySelector = getCountrySelector(phoneNumber); + } catch (InvalidPhoneNumberException e) { + return false; + } + + if (!isPhoneNumberValid(phoneNumber, countrySelector)) { + return false; + } + + if ((provider.length == 2) && (!countrySelector.equals(provider[0]))) { + return false; + } + + return true; + } + + private String getCountryCode(String phoneNumber) { + + PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); + try { + Phonenumber.PhoneNumber numberProto = phoneUtil.parse("+" + phoneNumber, ""); + logger.debug("Country code found: {}", numberProto.getCountryCode()); + return Integer.toString(numberProto.getCountryCode()); + } catch (NumberParseException e) { + throw new InvalidPhoneNumberException("No country code found for phone number: " + phoneNumber); + } + } + + public String getNormalizedPhoneNumber(String rawPhoneNumber) throws InvalidPhoneNumberException { + String[] provider = rawPhoneNumber.split(":"); + if (provider == null || provider.length > 2) { + throw new InvalidPhoneNumberException("Phone number with wrong format: " + rawPhoneNumber); + } + String country; + String phoneNumber; + if(provider.length == 1) { + country = getCountrySelector(provider[0]); + phoneNumber = provider[0]; + } else { + country = provider[0]; + phoneNumber = provider[1]; + } + logger.info("libphone: New phone number normalization request received - phone number: {}, country code: {}", + phoneNumber, country); + + PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); + String normalizedPhoneNumber = ""; + try { + PhoneNumber pn = phoneUtil.parse(phoneNumber, country); + normalizedPhoneNumber = Integer.toString(pn.getCountryCode()) + Long.toString(pn.getNationalNumber()); + logger.info("libphone: Normalized phone number: " + normalizedPhoneNumber); + } catch (NumberParseException e) { + logger.error("libphone: NumberParseException was thrown: {}", e.toString()); + logger.debug("libphone: NumberParseException was thrown: {}", e.getCause()); + } + return normalizedPhoneNumber; + } + + private String getCountrySelector(String phoneNumber) throws InvalidPhoneNumberException{ + String countryCode; + countryCode = getCountryCode(phoneNumber); + + CountryInfo countryInfo = countryMapByCountryCode.get(countryCode); + if (countryInfo != null) { + return countryInfo.getCountryCode(); + } else { + logger.debug("Country selector not found in'countries.txt' for country code: {}", countryCode); + throw new InvalidPhoneNumberException("No country found for code: " + countryCode); + } + } + +} diff --git a/src/main/java/biz/nynja/account/repositories/AccountByAuthenticationProviderRepository.java b/src/main/java/biz/nynja/account/repositories/AccountByAuthenticationProviderRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..73f9cb740a1e0036e686a5f2a2b92d965712198f --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/AccountByAuthenticationProviderRepository.java @@ -0,0 +1,17 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import java.util.List; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +import biz.nynja.account.models.AccountByAuthenticationProvider; + +@Repository +public interface AccountByAuthenticationProviderRepository extends CassandraRepository { + + List findAllByAuthenticationProvider(String authenticationProvider); +} diff --git a/src/main/java/biz/nynja/account/repositories/AccountByCommunicationProviderRepository.java b/src/main/java/biz/nynja/account/repositories/AccountByCommunicationProviderRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..bd96fcb5e1ce58f84f4939e27297c963e825c7f5 --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/AccountByCommunicationProviderRepository.java @@ -0,0 +1,16 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import java.util.UUID; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +import biz.nynja.account.models.AccountByCommunicationProvider; + +@Repository +public interface AccountByCommunicationProviderRepository extends CassandraRepository { + +} diff --git a/src/main/java/biz/nynja/account/repositories/AccountByProfileIdRepository.java b/src/main/java/biz/nynja/account/repositories/AccountByProfileIdRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..6cc2785f18d5ce20e3b6b556c421ad477f18af34 --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/AccountByProfileIdRepository.java @@ -0,0 +1,19 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import java.util.List; +import java.util.UUID; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +import biz.nynja.account.models.AccountByProfileId; + +@Repository +public interface AccountByProfileIdRepository extends CassandraRepository { + + List findAllByProfileId(UUID profileId); + +} diff --git a/src/main/java/biz/nynja/account/repositories/AccountByQrCodeRepository.java b/src/main/java/biz/nynja/account/repositories/AccountByQrCodeRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..0d893fb77a39823a8e2c926241c75320e3d30a9c --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/AccountByQrCodeRepository.java @@ -0,0 +1,17 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +import biz.nynja.account.models.AccountByQrCode; +import biz.nynja.account.models.AccountByUsername; + +@Repository +public interface AccountByQrCodeRepository extends CassandraRepository { + + AccountByQrCode findByQrCode(String qrCode); + +} diff --git a/src/main/java/biz/nynja/account/repositories/AccountByUsernameRepository.java b/src/main/java/biz/nynja/account/repositories/AccountByUsernameRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..647a9188989fb1c703a09f2302d521e3ac7d7eaf --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/AccountByUsernameRepository.java @@ -0,0 +1,16 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +import biz.nynja.account.models.AccountByUsername; + +@Repository +public interface AccountByUsernameRepository extends CassandraRepository { + + AccountByUsername findByUsername(String username); + +} diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepository.java b/src/main/java/biz/nynja/account/repositories/AccountRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..6dd80547dc3a5fff699856e8c60f269d5b0711f6 --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/AccountRepository.java @@ -0,0 +1,18 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import java.util.UUID; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +import biz.nynja.account.models.Account; + +@Repository +public interface AccountRepository extends CassandraRepository { + + Account findByAccountId(UUID accountId); + +} diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java new file mode 100644 index 0000000000000000000000000000000000000000..3503b4236be83bffa281342e3d73a454e54f8d73 --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import java.util.UUID; + +import org.springframework.stereotype.Repository; + +import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; +import biz.nynja.account.grpc.UpdateAccountRequest; +import biz.nynja.account.grpc.UpdateProfileRequest; +import biz.nynja.account.models.Account; +import biz.nynja.account.models.AuthenticationProvider; +import biz.nynja.account.models.ContactInfo; +import biz.nynja.account.models.PendingAccountByAuthenticationProvider; +import biz.nynja.account.models.Profile; + +@Repository +public interface AccountRepositoryAdditional { + + Account completePendingAccountCreation(CompletePendingAccountCreationRequest request); + + Account updateAccount(UpdateAccountRequest request); + + Profile updateProfile(UpdateProfileRequest request); + + boolean deleteAccount(UUID accountId); + + boolean deleteProfile(UUID profileId); + + boolean foundExistingNotOwnUsername(UUID accountId, String username); + + PendingAccountByAuthenticationProvider findSameAuthenticationProviderInPendingAccount(AuthenticationProvider authProvider); + + boolean authenticationProviderAlreadyUsedInAccount(AuthenticationProvider authProvider); + + boolean addAuthenticationProvider(UUID profileId, AuthenticationProvider authProvider); + + boolean deleteAuthenticationProvider(Profile profile, AuthenticationProvider authProvider); + + boolean addContactInfo(UUID accountId, ContactInfo contactInfo); + + boolean deleteContactInfo(UUID accountId, ContactInfo contactInfo); + + boolean editContactInfo(UUID accountId, ContactInfo oldContactInfo, ContactInfo editedContactInfo); +} diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..3254ff1b73434172817e16fac86e184db66537df --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -0,0 +1,657 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.cassandra.core.CassandraBatchOperations; +import org.springframework.data.cassandra.core.CassandraTemplate; +import org.springframework.data.cassandra.core.UpdateOptions; +import org.springframework.data.cassandra.core.WriteResult; +import org.springframework.stereotype.Service; + +import com.datastax.driver.core.BatchStatement; +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; + +import biz.nynja.account.components.AccountServiceHelper; +import biz.nynja.account.components.StatementsPool; +import biz.nynja.account.components.Validator; +import biz.nynja.account.grpc.AccessStatus; +import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; +import biz.nynja.account.grpc.UpdateAccountRequest; +import biz.nynja.account.grpc.UpdateProfileRequest; +import biz.nynja.account.models.Account; +import biz.nynja.account.models.AccountByAuthenticationProvider; +import biz.nynja.account.models.AccountByProfileId; +import biz.nynja.account.models.AccountByUsername; +import biz.nynja.account.models.AuthenticationProvider; +import biz.nynja.account.models.ContactInfo; +import biz.nynja.account.models.PendingAccount; +import biz.nynja.account.models.PendingAccountByAuthenticationProvider; +import biz.nynja.account.models.Profile; +import biz.nynja.account.models.ProfileByAuthenticationProvider; + +@Service +public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditional { + + private static final Logger logger = LoggerFactory.getLogger(AccountRepositoryAdditionalImpl.class); + + @Value("${complete.pending.account.timeout-minutes}") + private int COMPLETE_PENDING_ACCOUNT_TIMEOUT_IN_MINUTES; + + @Autowired + private CassandraTemplate cassandraTemplate; + + @Autowired + private StatementsPool statementsPool; + + @Autowired + private AccountRepository accountRepository; + + @Autowired + private ProfileRepository profileRepository; + + @Autowired + private AccountByUsernameRepository accountByUsernameRepository; + + @Autowired + private AccountByAuthenticationProviderRepository accountByAuthenticationProviderRepository; + + @Autowired + private AccountByProfileIdRepository accountByProfileIdRepository; + + @Autowired + private ProfileByAuthenticationProviderRepository profileByAuthenticationProviderRepository; + + @Autowired + private PendingAccountByAuthenticationProviderRepository pendingAccountByAuthenticationProviderRepository; + + @Autowired + private PendingAccountRepository pendingAccountRepository; + + @Autowired + private AccountServiceHelper accountServiceHelper; + + @Autowired + private Validator validator; + + @Autowired + private Session session; + + public Account completePendingAccountCreation(CompletePendingAccountCreationRequest request) { + CassandraBatchOperations batchOperations = cassandraTemplate.batchOps(); + PendingAccount pendingAccount = pendingAccountRepository + .findByAccountId(UUID.fromString(request.getAccountId())); + if (pendingAccount == null) { + logger.info("Existing pending account with the provided id was not found."); + logger.debug("Existing pending account with the provided id {} was not found.", request.getAccountId()); + return null; + } + Long timeCreated = Instant.now().toEpochMilli(); + Long checkMinutes = timeCreated - pendingAccount.getCreationTimestamp(); + if (checkMinutes > COMPLETE_PENDING_ACCOUNT_TIMEOUT_IN_MINUTES * 60 * 1000) { + logger.info("Account creation timeout expired."); + pendingAccountRepository.deleteById(UUID.fromString(request.getAccountId())); + return null; + } + WriteResult wr = null; + try { + newAccountInsert(batchOperations, request, pendingAccount, timeCreated); + newProfileInsert(batchOperations, request, pendingAccount, timeCreated); + newProfileByAuthenticationProviderInsert(batchOperations, pendingAccount); + wr = batchOperations.execute(); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.info("Exception while completing pending account creation."); + logger.debug("Exception while completing pending account creation: {} ...", e.getMessage()); + return null; + } + if (wr != null) { + boolean applied = wr.wasApplied(); + if (applied) { + pendingAccountRepository.deleteById(UUID.fromString(request.getAccountId())); + Account createdAccount = accountRepository.findByAccountId(UUID.fromString(request.getAccountId())); + return createdAccount; + } + } + return null; + } + + private void newAccountInsert(CassandraBatchOperations batchOps, CompletePendingAccountCreationRequest request, + PendingAccount pendingAccount, Long creationTimestamp) { + Account newAccount = new Account(); + newAccount.setAccountId(pendingAccount.getAccountId()); + newAccount.setProfileId(pendingAccount.getProfileId()); + newAccount.setAccountMark(request.getAccountMark()); + newAccount.setAuthenticationProvider(pendingAccount.getAuthenticationProvider()); + newAccount.setAuthenticationProviderType(pendingAccount.getAuthenticationProviderType()); + newAccount.setFirstName(request.getFirstName()); + newAccount.setLastName(request.getLastName()); + newAccount.setAvatar(request.getAvatar().asReadOnlyByteBuffer()); + newAccount.setAccountName(request.getAccountName()); + newAccount.setUsername(request.getUsername()); + newAccount.setCreationTimestamp(creationTimestamp); + newAccount.setQrCode(request.getQrCode()); + newAccount.setRoles(request.getRolesList().stream().map(n -> n.toString()).collect(Collectors.toSet())); + newAccount.setAccessStatus(AccessStatus.ENABLED.toString()); + batchOps.insert(newAccount); + } + + private void newProfileInsert(CassandraBatchOperations batchOps, CompletePendingAccountCreationRequest request, + PendingAccount pendingAccount, Long creationTimestamp) { + Profile newProfile = new Profile(); + newProfile.setProfileId(pendingAccount.getProfileId()); + Set authenticationProvidersSet = new HashSet(); + authenticationProvidersSet.add(AuthenticationProvider.createAuthenticationProviderFromStrings( + pendingAccount.getAuthenticationProviderType(), pendingAccount.getAuthenticationProvider())); + newProfile.setAuthenticationProviders(authenticationProvidersSet); + newProfile.setCreationTimestamp(creationTimestamp); + batchOps.insert(newProfile); + } + + private void newProfileByAuthenticationProviderInsert(CassandraBatchOperations batchOps, + PendingAccount pendingAccount) { + ProfileByAuthenticationProvider newProfileByAuthenticationProvider = new ProfileByAuthenticationProvider(); + newProfileByAuthenticationProvider.setAuthenticationProvider(pendingAccount.getAuthenticationProvider()); + newProfileByAuthenticationProvider + .setAuthenticationProviderType(pendingAccount.getAuthenticationProviderType()); + newProfileByAuthenticationProvider.setProfileId(pendingAccount.getProfileId()); + batchOps.insert(newProfileByAuthenticationProvider); + } + + public Profile updateProfile(UpdateProfileRequest request) { + CassandraBatchOperations batchOperations = cassandraTemplate.batchOps(); + Profile existingProfile = profileRepository.findByProfileId(UUID.fromString(request.getProfileId())); + if (existingProfile == null) { + logger.info("Existing profile with the provided id was not found."); + logger.debug("Existing profile with the provided id {} was not found.", request.getProfileId()); + return null; + } + Long timeUpdated = Instant.now().toEpochMilli(); + WriteResult wr = null; + try { + updateProfileData(batchOperations, request, existingProfile, timeUpdated); + wr = batchOperations.execute(); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.info("Exception while updating profile."); + logger.debug("Exception while updating profile: {} ...", e.getMessage()); + return null; + } + + if (wr != null) { + boolean applied = wr.wasApplied(); + if (applied) { + Profile updatedProfile = profileRepository.findByProfileId(UUID.fromString(request.getProfileId())); + return updatedProfile; + } + } + return null; + } + + public Account updateAccount(UpdateAccountRequest request) { + CassandraBatchOperations batchOperations = cassandraTemplate.batchOps(); + Account existingAccount = accountRepository.findByAccountId(UUID.fromString(request.getAccountId())); + if (existingAccount == null) { + logger.error("Existing account with the provided id {} was not found.", request.getAccountId()); + logger.debug("Existing account with the provided id {} was not found.", request.getAccountId()); + return null; + } + Long timeUpdated = Instant.now().toEpochMilli(); + WriteResult wr = null; + try { + try { + updateAccountData(batchOperations, request, existingAccount, timeUpdated); + } catch (DateTimeException e) { + logger.error("Exception with birthday date while updating account with id {}", request.getAccountId()); + return null; + } + wr = batchOperations.execute(); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.error("Exception while updating account with id {}.", request.getAccountId()); + logger.debug("Exception while updating account: {}.", e.getMessage()); + return null; + } + + if (wr != null) { + boolean applied = wr.wasApplied(); + if (applied) { + Account updatedAccount = accountRepository.findByAccountId(UUID.fromString(request.getAccountId())); + return updatedAccount; + } + } + return null; + } + + public boolean deleteAccount(UUID accountId) { + CassandraBatchOperations batchOperations = cassandraTemplate.batchOps(); + Account existingAccount = accountRepository.findByAccountId(accountId); + if (!doExistAccountAndProfileToDelete(accountId)) { + return false; + } + Profile existingProfile = profileRepository.findByProfileId(existingAccount.getProfileId()); + List existingAccountsForProfile = accountByProfileIdRepository + .findAllByProfileId(existingAccount.getProfileId()); + if (existingAccountsForProfile == null) { + logger.error("Error deleting account. Existing accounts for the given profile id {} were not found.", + existingAccount.getProfileId()); + return false; + } + boolean alsoDeleteProfile = false; + // check for last account in the profile + if (existingAccountsForProfile.size() == 1) { + alsoDeleteProfile = true; + } + WriteResult wr = null; + try { + deleteAccountData(batchOperations, existingAccount); + deleteProfileByAuthenticationProvider(batchOperations, existingAccount.getProfileId(), + AuthenticationProvider.createAuthenticationProviderFromStrings( + existingAccount.getAuthenticationProviderType(), + existingAccount.getAuthenticationProvider())); + if (alsoDeleteProfile) { + if (existingProfile.getAuthenticationProviders() != null) { + deleteAuthenticationProvidersFromProfile(batchOperations, existingProfile.getProfileId(), + existingProfile.getAuthenticationProviders()); + } + deleteProfileData(batchOperations, existingProfile); + } else { + if (!removeCreationProvider(batchOperations, existingAccount, existingProfile)) { + return false; + } + } + wr = batchOperations.execute(); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.error("Exception while deleting account: {}.", e.getMessage()); + return false; + } + if (wr != null && wr.wasApplied()) { + return true; + } + return false; + } + + /** + * The method removes the deleted account's creation provider from the list of auth providers of the profile (if not + * already manually removed by the user). + * + * @param batchOperations + * @param existingAccount + * @param existingProfile + * @return true or false according to the result of removing the account's creation provider + */ + private boolean removeCreationProvider(CassandraBatchOperations batchOperations, Account existingAccount, + Profile existingProfile) { + // update authentication providers of the profile by removing the one of the deleted account (if not + // already manually removed by the user) + Long timeUpdated = Instant.now().toEpochMilli(); + if (existingProfile.getAuthenticationProviders() != null) { + Set existingAuthenticationProvidersSet = new HashSet( + existingProfile.getAuthenticationProviders()); + boolean removedAuthProvider = existingAuthenticationProvidersSet.remove(AuthenticationProvider + .createAuthenticationProviderFromStrings(existingAccount.getAuthenticationProviderType(), + existingAccount.getAuthenticationProvider())); + if (removedAuthProvider) { + // at least one authentication provider should exist in the profile + if (existingAuthenticationProvidersSet.size() > 0) { + updateAuthProvidersInProfileWhenDeletingAccount(batchOperations, existingProfile, + existingAuthenticationProvidersSet, timeUpdated); + } else { + logger.error( + "Error deleting account. At least one authentication provider should exist in profile: {}.", + existingAccount.getProfileId()); + return false; + } + } + } + return true; + } + + private void updateAccountData(CassandraBatchOperations batchOps, UpdateAccountRequest request, + Account existingAccount, Long lastUpdateTimestamp) { + Account updatedAccount = existingAccount; + updatedAccount.setAvatar(request.getAvatar().asReadOnlyByteBuffer()); + updatedAccount.setAccountMark(request.getAccountMark()); + updatedAccount.setAccountName(request.getAccountName()); + updatedAccount.setFirstName(request.getFirstName()); + updatedAccount.setLastName(request.getLastName()); + updatedAccount.setUsername(request.getUsername()); + updatedAccount.setLastUpdateTimestamp(lastUpdateTimestamp); + updatedAccount.setAccessStatus(request.getAccessStatus().toString()); + if (validator.validateBirthdayIsSet(request.getBirthday())) { + updatedAccount.setBirthday(LocalDate.of(request.getBirthday().getYear(), request.getBirthday().getMonth(), + request.getBirthday().getDay())); + } else { + updatedAccount.setBirthday(null); + } + batchOps.update(updatedAccount); + } + + private void updateProfileData(CassandraBatchOperations batchOps, UpdateProfileRequest request, + Profile existingProfile, Long lastUpdateTimestamp) { + Profile updatedProfile = existingProfile; + updatedProfile.setPasscode(request.getPasscode()); + if (!request.getDefaultAccountId().trim().isEmpty()) { + updatedProfile.setDefaultAccount(UUID.fromString(request.getDefaultAccountId())); + } else { + updatedProfile.setDefaultAccount(null); + } + updatedProfile.setLastUpdateTimestamp(lastUpdateTimestamp); + batchOps.update(updatedProfile); + } + + private void updateAuthProvidersInProfileWhenDeletingAccount(CassandraBatchOperations batchOps, + Profile existingProfile, Set authProvidersToUpdate, Long lastUpdateTimestamp) { + Profile updatedProfile = existingProfile; + if (authProvidersToUpdate != null) { + updatedProfile.setAuthenticationProviders(authProvidersToUpdate); + updatedProfile.setLastUpdateTimestamp(lastUpdateTimestamp); + batchOps.update(updatedProfile); + } + } + + private boolean doExistAccountAndProfileToDelete(UUID accountId) { + Account existingAccount = accountRepository.findByAccountId(accountId); + if (existingAccount == null) { + logger.error("Error deleting account. Existing account with the provided id {} was not found.", accountId); + return false; + } + Profile existingProfile = profileRepository.findByProfileId(existingAccount.getProfileId()); + if (existingProfile == null) { + logger.error("Error deleting account. Corresponding profile with the provided id {} was not found.", + existingAccount.getProfileId()); + return false; + } + return true; + } + + private void deleteAccountData(CassandraBatchOperations batchOps, Account existingAccountToDelete) { + Account accountToDelete = existingAccountToDelete; + batchOps.delete(accountToDelete); + } + + private void deleteProfileData(CassandraBatchOperations batchOps, Profile existingProfileToDelete) { + Profile profileToDelete = existingProfileToDelete; + batchOps.delete(profileToDelete); + } + + private void deleteAuthenticationProvidersFromProfile(CassandraBatchOperations batchOps, UUID profileId, + Set authenticationProvidersSetToDelete) { + for (AuthenticationProvider authProvider : authenticationProvidersSetToDelete) { + deleteProfileByAuthenticationProvider(batchOps, profileId, authProvider); + } + } + + private void deleteProfileByAuthenticationProvider(CassandraBatchOperations batchOps, UUID profileId, + AuthenticationProvider authProvider) { + ProfileByAuthenticationProvider profileByAuthenticationProviderToDelete = new ProfileByAuthenticationProvider(); + profileByAuthenticationProviderToDelete.setAuthenticationProvider(authProvider.getValue()); + profileByAuthenticationProviderToDelete.setAuthenticationProviderType(authProvider.getType()); + profileByAuthenticationProviderToDelete.setProfileId(profileId); + batchOps.delete(profileByAuthenticationProviderToDelete); + } + + public boolean foundExistingNotOwnUsername(UUID accountId, String username) { + AccountByUsername foundAccountByUsername = accountByUsernameRepository.findByUsername(username); + if (foundAccountByUsername == null) { + return false; + } else if (!foundAccountByUsername.getAccountId().equals(accountId)) { + return true; + } else { + return false; + } + } + + public PendingAccountByAuthenticationProvider findSameAuthenticationProviderInPendingAccount( + AuthenticationProvider authProvider) { + List pendingAccounts = pendingAccountByAuthenticationProviderRepository + .findAllByAuthenticationProvider(authProvider.getValue()); + if (pendingAccounts.isEmpty()) { + return null; + } + + // Both authentication provider identifier and type uniquely identify the authentication provider in DB. + // For this reason we need to filter results by authentication provider type. + for (PendingAccountByAuthenticationProvider pendingAccount : pendingAccounts) { + if (pendingAccount.getAuthenticationProviderType().equals(authProvider.getType())) { + return pendingAccount; + } + } + return null; + } + + public boolean authenticationProviderAlreadyUsedInAccount(AuthenticationProvider authProvider) { + List accounts = accountByAuthenticationProviderRepository + .findAllByAuthenticationProvider(authProvider.getValue()); + + if (accounts.isEmpty()) { + return false; + } + + // Both authentication provider identifier and type uniquely identify the authentication provider in DB. + // For this reason we need to filter results by authentication provider type. + for (AccountByAuthenticationProvider account : accounts) { + if (account.getAuthenticationProviderType().equals(authProvider.getType())) { + return true; + } + } + return false; + } + + public boolean deleteProfile(UUID profileId) { + CassandraBatchOperations batchOperations = cassandraTemplate.batchOps(); + Profile existingProfile = profileRepository.findByProfileId(profileId); + if (existingProfile == null) { + logger.error("Error deleting profile. Existing profile with the provided id {} was not found.", profileId); + return false; + } + List existingAccountsForProfile = accountByProfileIdRepository + .findAllByProfileId(profileId); + WriteResult wr = null; + try { + deleteProfileAccountsWhenDeletingProfile(profileId, batchOperations, existingAccountsForProfile); + if (existingProfile.getAuthenticationProviders() != null) { + deleteAuthenticationProvidersFromProfile(batchOperations, existingProfile.getProfileId(), + existingProfile.getAuthenticationProviders()); + } + deleteProfileData(batchOperations, existingProfile); + wr = batchOperations.execute(); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.info("Exception while deleting account."); + logger.debug("Exception while deleting account: {} ...", e.getMessage()); + return false; + } + if (wr != null && wr.wasApplied()) { + return true; + } + return false; + } + + private void deleteProfileAccountsWhenDeletingProfile(UUID profileId, CassandraBatchOperations batchOperations, + List existingAccountsForProfile) { + for (AccountByProfileId accountByProfileId : existingAccountsForProfile) { + Account existingAccount = accountRepository.findByAccountId(accountByProfileId.getAccountId()); + deleteAccountData(batchOperations, existingAccount); + deleteProfileByAuthenticationProvider(batchOperations, profileId, + AuthenticationProvider.createAuthenticationProviderFromStrings( + existingAccount.getAuthenticationProviderType(), + existingAccount.getAuthenticationProvider())); + } + } + + public boolean authenticationProviderAlreadyUsedInProfile(AuthenticationProvider authProvider) { + ProfileByAuthenticationProvider profile = profileByAuthenticationProviderRepository + .findByAuthenticationProviderAndAuthenticationProviderType(authProvider.getValue(), + authProvider.getType()); + if (profile != null) { + return true; + } + return false; + } + + @Override + public boolean addAuthenticationProvider(UUID profileId, AuthenticationProvider authProvider) { + BatchStatement batch = new BatchStatement(); + ResultSet wr = null; + try { + BoundStatement updateAuthProvidersInProfile = statementsPool.addAuthenticationProviderToProfile(profileId, + authProvider); + batch.add(updateAuthProvidersInProfile); + + BoundStatement insertInProfileByAuthProvider = statementsPool + .insertProfileByAuthenticationProvider(profileId, authProvider.getValue(), authProvider.getType()); + batch.add(insertInProfileByAuthProvider); + + wr = session.execute(batch); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.info("Exception while adding new authenication provider to profile with id: {}.", profileId); + logger.debug("Exception while adding new authenication provider to profile with id: {}, {}", profileId, + e.getMessage()); + return false; + } + + if (wr != null) { + boolean applied = wr.wasApplied(); + if (applied) { + return true; + } + } + return false; + } + + @Override + public boolean addContactInfo(UUID accountId, ContactInfo contactInfo) { + Account accountToUpdate = accountRepository.findByAccountId(accountId); + if (accountToUpdate == null) { + logger.error("Existing account with the provided id {} was not found.", accountId); + return false; + } + Set contactsInfoSet = accountToUpdate.getContactsInfo(); + if (contactsInfoSet == null) { + contactsInfoSet = new HashSet(); + } + if (contactInfo != null) { + if (!contactsInfoSet.add(contactInfo)) { + logger.error("Error adding contact info to account {}. {} already exists.", accountId, + contactInfo.toString()); + return false; + } + accountToUpdate.setContactsInfo(contactsInfoSet); + } + Long timeUpdated = Instant.now().toEpochMilli(); + accountToUpdate.setLastUpdateTimestamp(timeUpdated); + WriteResult wr = cassandraTemplate.update(accountToUpdate, UpdateOptions.builder().ifExists(true).build()); + if (wr != null && wr.wasApplied()) { + return true; + } + return false; + } + + @Override + public boolean deleteContactInfo(UUID accountId, ContactInfo contactInfo) { + Account accountToUpdate = accountRepository.findByAccountId(accountId); + if (accountToUpdate == null) { + logger.error("Existing account with the provided id {} was not found.", accountId); + return false; + } + Set contactsInfoSet = accountToUpdate.getContactsInfo(); + if (contactsInfoSet == null) { + logger.error("No existing contact info found for account {}.", accountId); + return false; + } + if (!contactsInfoSet.remove(contactInfo)) { + logger.error("Error removing contact info from account {}. Existing contact info: {} was not found.", + accountId, contactInfo.toString()); + return false; + } + accountToUpdate.setContactsInfo(contactsInfoSet); + Long timeUpdated = Instant.now().toEpochMilli(); + accountToUpdate.setLastUpdateTimestamp(timeUpdated); + WriteResult wr = cassandraTemplate.update(accountToUpdate, UpdateOptions.builder().ifExists(true).build()); + if (wr != null && wr.wasApplied()) { + return true; + } + return false; + } + + @Override + public boolean deleteAuthenticationProvider(Profile profile, AuthenticationProvider authProvider) { + + BatchStatement batch = new BatchStatement(); + ResultSet rs = null; + + // Remove authentication provider form list of authentication providers in profile + BoundStatement deleteAuthProviderStatement = statementsPool + .deleteAuthenicationProviderFromProfile(profile.getProfileId(), authProvider); + batch.add(deleteAuthProviderStatement); + + // Remove record for profile by this authentication provider + BoundStatement deleteProfileStatement = statementsPool + .deleteProfileByAuthenticationProvider(authProvider.getValue(), authProvider.getType()); + batch.add(deleteProfileStatement); + + // Check if there is an account, created using this authentication provider + Account account = accountServiceHelper.getAccountByAuthenticationProviderHelper(authProvider.getValue(), + authProvider.getType()); + + // Delete authentication provider from account + if (account != null) { + BoundStatement deleteCreationProviderStatement = statementsPool + .deleteCreationProviderFromAccount(account.getAccountId()); + batch.add(deleteCreationProviderStatement); + } + + rs = session.execute(batch); + if (rs != null) { + boolean applied = rs.wasApplied(); + if (applied) { + return true; + } + } + return false; + } + + @Override + public boolean editContactInfo(UUID accountId, ContactInfo oldContactInfo, ContactInfo editedContactInfo) { + Account accountToUpdate = accountRepository.findByAccountId(accountId); + if (accountToUpdate == null) { + logger.error("Existing account with the provided id {} was not found.", accountId); + return false; + } + Set contactsInfoSet = accountToUpdate.getContactsInfo(); + if (contactsInfoSet == null) { + logger.error("No existing contact info found for account {}.", accountId); + return false; + } + if (!contactsInfoSet.remove(oldContactInfo)) { + logger.error("Error removing contact info from account {}. Existing contact info: {} was not found.", + accountId, oldContactInfo.toString()); + return false; + } + if (!contactsInfoSet.add(editedContactInfo)) { + logger.error("Error adding contact info to account {}. {} already exists.", accountId, + editedContactInfo.toString()); + return false; + } + accountToUpdate.setContactsInfo(contactsInfoSet); + Long timeUpdated = Instant.now().toEpochMilli(); + accountToUpdate.setLastUpdateTimestamp(timeUpdated); + WriteResult wr = cassandraTemplate.update(accountToUpdate, UpdateOptions.builder().ifExists(true).build()); + if (wr != null && wr.wasApplied()) { + return true; + } + return false; + } +} diff --git a/src/main/java/biz/nynja/account/repositories/PendingAccountByAuthenticationProviderRepository.java b/src/main/java/biz/nynja/account/repositories/PendingAccountByAuthenticationProviderRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..7648c4bb0c5c5b64e44098549706d278af804025 --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/PendingAccountByAuthenticationProviderRepository.java @@ -0,0 +1,18 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import java.util.List; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +import biz.nynja.account.models.PendingAccountByAuthenticationProvider; + +@Repository +public interface PendingAccountByAuthenticationProviderRepository extends CassandraRepository { + + List findAllByAuthenticationProvider(String authenticationProvider); + +} diff --git a/src/main/java/biz/nynja/account/repositories/PendingAccountRepository.java b/src/main/java/biz/nynja/account/repositories/PendingAccountRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..4d3c6f1eec8766ea9cef3ffbe029d9eb2970eb3e --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/PendingAccountRepository.java @@ -0,0 +1,19 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import java.util.UUID; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +import biz.nynja.account.models.Account; +import biz.nynja.account.models.PendingAccount; + +@Repository +public interface PendingAccountRepository extends CassandraRepository { + + PendingAccount findByAccountId(UUID accountId); + +} diff --git a/src/main/java/biz/nynja/account/repositories/ProfileByAuthenticationProviderRepository.java b/src/main/java/biz/nynja/account/repositories/ProfileByAuthenticationProviderRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..3ab43dc5919064db1854bded76132351fd6457fb --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/ProfileByAuthenticationProviderRepository.java @@ -0,0 +1,17 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +import biz.nynja.account.models.ProfileByAuthenticationProvider; + +@Repository +public interface ProfileByAuthenticationProviderRepository + extends CassandraRepository { + + ProfileByAuthenticationProvider findByAuthenticationProviderAndAuthenticationProviderType(String authProvider, + String authProviderType); +} diff --git a/src/main/java/biz/nynja/account/repositories/ProfileRepository.java b/src/main/java/biz/nynja/account/repositories/ProfileRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..94b350f27763923a796105280c0cacc6607cd693 --- /dev/null +++ b/src/main/java/biz/nynja/account/repositories/ProfileRepository.java @@ -0,0 +1,18 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.repositories; + +import java.util.UUID; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +import biz.nynja.account.models.Profile; + +@Repository +public interface ProfileRepository extends CassandraRepository { + + Profile findByProfileId(UUID profileId); + +} diff --git a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..69bb074afcb60748e33aa05829688210fe08342a --- /dev/null +++ b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java @@ -0,0 +1,782 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.services; + +import java.nio.ByteBuffer; +import java.util.Optional; +import java.util.UUID; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.lognet.springboot.grpc.GRpcService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import biz.nynja.account.components.Validator; +import biz.nynja.account.grpc.AccountByAccountIdRequest; +import biz.nynja.account.grpc.AccountByAuthenticationProviderRequest; +import biz.nynja.account.grpc.AccountResponse; +import biz.nynja.account.grpc.AccountServiceGrpc; +import biz.nynja.account.grpc.AccountsByProfileIdRequest; +import biz.nynja.account.grpc.AccountsResponse; +import biz.nynja.account.grpc.AddAuthenticationProviderRequest; +import biz.nynja.account.grpc.AddContactInfoRequest; +import biz.nynja.account.grpc.AuthProviderDetails; +import biz.nynja.account.grpc.AuthenticationType; +import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; +import biz.nynja.account.grpc.ContactType; +import biz.nynja.account.grpc.CreatePendingAccountRequest; +import biz.nynja.account.grpc.CreatePendingAccountResponse; +import biz.nynja.account.grpc.DeleteAccountRequest; +import biz.nynja.account.grpc.DeleteAuthenticationProviderRequest; +import biz.nynja.account.grpc.DeleteContactInfoRequest; +import biz.nynja.account.grpc.DeleteProfileRequest; +import biz.nynja.account.grpc.EditContactInfoRequest; +import biz.nynja.account.grpc.ErrorResponse; +import biz.nynja.account.grpc.ErrorResponse.Cause; +import biz.nynja.account.grpc.GetByEmailRequest; +import biz.nynja.account.grpc.GetByPhoneNumberRequest; +import biz.nynja.account.grpc.GetByQrCodeRequest; +import biz.nynja.account.grpc.GetByUsernameRequest; +import biz.nynja.account.grpc.ProfileResponse; +import biz.nynja.account.grpc.SearchResponse; +import biz.nynja.account.grpc.SearchResultDetails; +import biz.nynja.account.grpc.StatusResponse; +import biz.nynja.account.grpc.UpdateAccountRequest; +import biz.nynja.account.models.Account; +import biz.nynja.account.models.AccountByQrCode; +import biz.nynja.account.models.AccountByUsername; +import biz.nynja.account.models.AuthenticationProvider; +import biz.nynja.account.models.ContactInfo; +import biz.nynja.account.models.Profile; +import biz.nynja.account.models.ProfileByAuthenticationProvider; +import biz.nynja.account.phone.PhoneNumberNormalizer; +import biz.nynja.account.phone.PhoneNumberValidator; +import biz.nynja.account.repositories.AccountByQrCodeRepository; +import biz.nynja.account.repositories.AccountByUsernameRepository; +import biz.nynja.account.repositories.AccountRepositoryAdditional; +import biz.nynja.account.repositories.ProfileByAuthenticationProviderRepository; +import biz.nynja.account.repositories.ProfileRepository; +import biz.nynja.account.services.decomposition.AccountCreator; +import biz.nynja.account.services.decomposition.AccountProvider; +import biz.nynja.account.validation.Validation; +import biz.nynja.account.validation.ValidationError; +import io.grpc.stub.StreamObserver; + +/** + * gRPC Account service implementation.
+ * The service extends the protobuf generated class and overrides the needed methods. It also saves/retrieves the + * account information via {@link AccountInfoRepository}. + */ +@GRpcService +public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBase { + + private static final Logger logger = LoggerFactory.getLogger(AccountServiceImpl.class); + private static final byte MIN_NUMBER_OF_AUTH_PROVIDERS_IN_PROFILE = 1; + + private final AccountRepositoryAdditional accountRepositoryAdditional; + private final ProfileRepository profileRepository; + private final ProfileByAuthenticationProviderRepository profileByAutheticationProviderRepository; + private final AccountByQrCodeRepository accountByQrCodeRepository; + private final AccountByUsernameRepository accountByUsernameRepository; + private final Validator validator; + private final PhoneNumberValidator phoneNumberValidator; + private final AccountProvider accountProvider; + private final PhoneNumberNormalizer phoneNumberNormalizer; + private final AccountCreator accountCreator; + + public AccountServiceImpl(AccountRepositoryAdditional accountRepositoryAdditional, + ProfileRepository profileRepository, + ProfileByAuthenticationProviderRepository profileByAutheticationProviderRepository, + AccountByQrCodeRepository accountByQrCodeRepository, + AccountByUsernameRepository accountByUsernameRepository, Validator validator, + PhoneNumberValidator phoneNumberValidator, AccountProvider accountProvider, + PhoneNumberNormalizer phoneNumberNormalizer, AccountCreator accountCreator) { + this.accountRepositoryAdditional = accountRepositoryAdditional; + this.profileRepository = profileRepository; + this.profileByAutheticationProviderRepository = profileByAutheticationProviderRepository; + this.accountByQrCodeRepository = accountByQrCodeRepository; + this.accountByUsernameRepository = accountByUsernameRepository; + this.validator = validator; + this.phoneNumberValidator = phoneNumberValidator; + this.accountProvider = accountProvider; + this.phoneNumberNormalizer = phoneNumberNormalizer; + this.accountCreator = accountCreator; + } + + @Override + public void getAccountByAuthenticationProvider(AccountByAuthenticationProviderRequest request, + StreamObserver responseObserver) { + + logger.info("Getting account by authentication provider: {}", request); + if (request.getAuthenticationTypeValue() == 0) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), + "Missing authentication provider", "", Cause.MISSING_AUTH_PROVIDER_TYPE); + return; + } + if (request.getAuthenticationIdentifier() == null || request.getAuthenticationIdentifier().isEmpty()) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), + "Missing authentication provider identifier", "", Cause.MISSING_AUTH_PROVIDER_IDENTIFIER); + return; + } + Optional account = accountProvider.getAccountResponseByAuthenticationProvider( + request.getAuthenticationType(), request.getAuthenticationIdentifier()); + + if (!account.isPresent()) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), + "Account not found for identifier: {}", request.getAuthenticationIdentifier(), + Cause.ACCOUNT_NOT_FOUND); + } else { + responseObserver.onNext(account.get()); + responseObserver.onCompleted(); + return; + } + + } + + @Override + public void searchByEmail(GetByEmailRequest request, StreamObserver responseObserver) { + logger.info("Search account by e-mail: {}", request.getEmail()); + if ((request.getEmail() == null) || request.getEmail().isEmpty()) { + logAndBuildGrpcSearchResponse(responseObserver, SearchResponse.newBuilder(), "Missing e-mail.", "", + Cause.MISSING_EMAIL); + return; + } + if (!validator.isEmailValid(request.getEmail())) { + logAndBuildGrpcSearchResponse(responseObserver, SearchResponse.newBuilder(), "Invalid e-mail!. Value : ", + request.getEmail(), Cause.EMAIL_INVALID); + return; + } + + Optional account = accountProvider.getAccountByAuthenticationProvider(AuthenticationType.EMAIL, + request.getEmail()); + + if (!account.isPresent()) { + logAndBuildGrpcSearchResponse(responseObserver, SearchResponse.newBuilder(), + "No matching accounts found for e-mail: ", request.getEmail(), Cause.EMAIL_NOT_FOUND); + return; + } else { + SearchResultDetails searchResultDetails = buildSearchResultDetails(account.get().getAccountId().toString(), + account.get().getAvatar(), account.get().getFirstName(), account.get().getLastName()); + + SearchResponse response = SearchResponse.newBuilder().setSearchResultDetails(searchResultDetails).build(); + logger.debug("Found result for account by e-mail {}: \"{}\"", request.getEmail(), response); + responseObserver.onNext(response); + responseObserver.onCompleted(); + return; + } + } + + @Override + public void searchByPhoneNumber(GetByPhoneNumberRequest request, StreamObserver responseObserver) { + + logger.info("Search account by phone: {}", request.getPhoneNumber()); + if ((request.getPhoneNumber() == null) || request.getPhoneNumber().isEmpty()) { + logAndBuildGrpcSearchResponse(responseObserver, SearchResponse.newBuilder(), "Missing phone number.", "", + Cause.MISSING_PHONENUMBER); + return; + } + + if (!phoneNumberValidator.isPhoneNumberValid(request.getPhoneNumber())) { + logAndBuildGrpcSearchResponse(responseObserver, SearchResponse.newBuilder(), + "Invalid phone number. Value : ", request.getPhoneNumber(), Cause.INVALID_PHONENUMBER); + return; + } + + Optional account = accountProvider.getAccountByAuthenticationProvider(AuthenticationType.PHONE, + request.getPhoneNumber()); + + if (!account.isPresent()) { + logAndBuildGrpcSearchResponse(responseObserver, SearchResponse.newBuilder(), + "No matching accounts found for phone: ", request.getPhoneNumber(), Cause.PHONENUMBER_NOT_FOUND); + return; + } else { + SearchResultDetails searchResultDetails = buildSearchResultDetails(account.get().getAccountId().toString(), + account.get().getAvatar(), account.get().getFirstName(), account.get().getLastName()); + + SearchResponse response = SearchResponse.newBuilder().setSearchResultDetails(searchResultDetails).build(); + logger.debug("Found result for account by phone {}: \"{}\"", request.getPhoneNumber(), response); + responseObserver.onNext(response); + responseObserver.onCompleted(); + return; + } + } + + @Override + public void getAccountByUsername(GetByUsernameRequest request, StreamObserver responseObserver) { + logger.info("Getting account by username: {}", request.getUsername()); + Validation validation = validateGetByUsernameRequest(request); + if (validation.hasErrors()) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), validation.getErrorMessage(), + "", validation.getCause().get()); + return; + } + + Optional accountResonse = accountProvider.getAccountResponseByUsername(request.getUsername()); + + if (!accountResonse.isPresent()) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), "Account not found", "", + Cause.ACCOUNT_NOT_FOUND); + return; + } else { + responseObserver.onNext(accountResonse.get()); + responseObserver.onCompleted(); + return; + } + } + + @Override + public void searchByUsername(GetByUsernameRequest request, StreamObserver responseObserver) { + logger.info("Searching account by username: {}", request.getUsername()); + Validation validation = validateGetByUsernameRequest(request); + if (validation.hasErrors()) { + logAndBuildGrpcSearchResponse(responseObserver, SearchResponse.newBuilder(), validation.getErrorMessage(), + "", validation.getCause().get()); + return; + } + + AccountByUsername account = accountByUsernameRepository.findByUsername(request.getUsername()); + if (account == null) { + logAndBuildGrpcSearchResponse(responseObserver, SearchResponse.newBuilder(), + "No matching accounts found for username: ", request.getUsername(), Cause.USERNAME_NOT_FOUND); + return; + } + + SearchResultDetails searchResultDetails = buildSearchResultDetails(account.getAccountId().toString(), + account.getAvatar(), account.getFirstName(), account.getLastName()); + + SearchResponse response = SearchResponse.newBuilder().setSearchResultDetails(searchResultDetails).build(); + logger.debug("Found result for account by username {}: \"{}\"", request.getUsername(), response); + responseObserver.onNext(response); + responseObserver.onCompleted(); + + return; + } + + private Validation validateGetByUsernameRequest(GetByUsernameRequest request) { + Validation validation = new Validation(); + + if ((request.getUsername() == null) || request.getUsername().isEmpty()) { + validation.addError(new ValidationError("Missing username.", Cause.MISSING_USERNAME)); + } + if (!validator.isValidUsername(request.getUsername())) { + validation.addError( + new ValidationError("Invalid username. Value: " + request.getUsername(), Cause.USERNAME_INVALID)); + } + + return validation; + } + + @Override + public void getAccountByQrCode(GetByQrCodeRequest request, StreamObserver responseObserver) { + logger.info("Search account by QR code: {}", request.getQrCode()); + if ((request.getQrCode() == null) || request.getQrCode().isEmpty()) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), "Missing QR code.", "", + Cause.MISSING_QR_CODE); + return; + } + + Optional accountResonse = accountProvider.getAccountResponseByQrCode(request.getQrCode()); + + if (!accountResonse.isPresent()) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), "Account not found", "", + Cause.ACCOUNT_NOT_FOUND); + return; + } else { + responseObserver.onNext(accountResonse.get()); + responseObserver.onCompleted(); + return; + } + + } + + @Override + public void searchByQrCode(GetByQrCodeRequest request, StreamObserver responseObserver) { + logger.info("Search account by QR code: {}", request.getQrCode()); + if ((request.getQrCode() == null) || request.getQrCode().isEmpty()) { + logAndBuildGrpcSearchResponse(responseObserver, SearchResponse.newBuilder(), "Missing QR code.", "", + Cause.MISSING_QR_CODE); + return; + } + + AccountByQrCode account = accountByQrCodeRepository.findByQrCode(request.getQrCode()); + if (account == null) { + logAndBuildGrpcSearchResponse(responseObserver, SearchResponse.newBuilder(), + "No matching accounts found for QR code! Value: ", request.getQrCode(), Cause.QR_CODE_NOT_FOUND); + return; + } + + SearchResultDetails searchResultDetails = buildSearchResultDetails(account.getAccountId().toString(), + account.getAvatar(), account.getFirstName(), account.getLastName()); + + SearchResponse response = SearchResponse.newBuilder().setSearchResultDetails(searchResultDetails).build(); + logger.debug("Found result for account by QR code {}: \"{}\"", request.getQrCode(), response); + responseObserver.onNext(response); + responseObserver.onCompleted(); + + return; + } + + @Override + public void getAllAccountsByProfileId(AccountsByProfileIdRequest request, + StreamObserver responseObserver) { + logger.info("Getting accounts by profile id: {}", request.getProfileId()); + + if ((request.getProfileId() == null) || (request.getProfileId().isEmpty())) { + logAndBuildGrpcAccountsResponse(responseObserver, AccountsResponse.newBuilder(), "Missing profile id", "", + Cause.MISSING_PROFILE_ID); + return; + } + if (!validator.isValidUuid(request.getProfileId())) { + logAndBuildGrpcAccountsResponse(responseObserver, AccountsResponse.newBuilder(), "Invalid profile id: {}", + request.getProfileId(), Cause.INVALID_PROFILE_ID); + return; + } + Optional accounts = accountProvider.getAllAccountsByProfileId(request); + + if (!accounts.isPresent()) { + logAndBuildGrpcAccountsResponse(responseObserver, AccountsResponse.newBuilder(), + "Account not found for profile id: {}", request.getProfileId(), Cause.ACCOUNT_NOT_FOUND); + } else { + responseObserver.onNext(accounts.get()); + responseObserver.onCompleted(); + return; + } + + } + + @Override + public void getAccountByAccountId(AccountByAccountIdRequest request, + StreamObserver responseObserver) { + logger.info("Getting accounts by account id: {}", request.getAccountId()); + + if ((request.getAccountId() == null) || (request.getAccountId().isEmpty())) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), "Missing account id", "", + Cause.MISSING_ACCOUNT_ID); + return; + } + + if (!validator.isValidUuid(request.getAccountId())) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), "Invalid account id: {}", + request.getAccountId(), Cause.INVALID_ACCOUNT_ID); + return; + } + + Optional account = accountProvider.getAccountByAccountId(request); + + if (!account.isPresent()) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), "Account id not found: {}", + request.getAccountId(), Cause.ACCOUNT_NOT_FOUND); + } else { + responseObserver.onNext(account.get()); + responseObserver.onCompleted(); + return; + } + } + + @Override + public void createPendingAccount(CreatePendingAccountRequest request, + StreamObserver responseObserver) { + + logger.info("Creating pending account..."); + logger.debug("Creating pending account: {} ...", request); + + CreatePendingAccountResponse response = accountCreator.retrieveCreatePendingAccountResponse(request); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + + @Override + public void completePendingAccountCreation(CompletePendingAccountCreationRequest request, + StreamObserver responseObserver) { + + logger.info("Complete pending account creation..."); + logger.debug("Complete pending account creation...: {} ...", request); + + AccountResponse response = accountCreator.retrieveCompletePendingAccountResponse(request); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + + @Override + public void updateAccount(UpdateAccountRequest request, StreamObserver responseObserver) { + logger.info("Updating account..."); + logger.debug("Updating account...: {}", request); + + if ((request.getAccountId() == null) || (request.getAccountId().isEmpty())) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), "Missing account id", "", + Cause.MISSING_ACCOUNT_ID); + return; + } + Cause cause = validator.validateUpdateAccountRequest(request); + if (cause != null) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), "Validation failed", "", + cause); + return; + } + + if (request.getUsername() != null && !request.getUsername().trim().isEmpty() && accountRepositoryAdditional + .foundExistingNotOwnUsername(UUID.fromString(request.getAccountId()), request.getUsername())) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), + "User name already in use: {}", request.getUsername(), Cause.USERNAME_ALREADY_USED); + return; + } + + Account updatedAccount = accountRepositoryAdditional.updateAccount(request); + + if (updatedAccount == null) { + logAndBuildGrpcAccountResponse(responseObserver, AccountResponse.newBuilder(), "Error updating account.", + "", Cause.ERROR_UPDATING_ACCOUNT); + return; + } + logger.debug("Account \"{}\" updated in the DB", updatedAccount.toString()); + logger.debug("Account: \"{}\" updated successfully.", updatedAccount); + AccountResponse response = AccountResponse.newBuilder().setAccountDetails(updatedAccount.toProto()).build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + logger.info("Account updated successfully."); + return; + } + + @Override + public void deleteAccount(DeleteAccountRequest request, StreamObserver responseObserver) { + logger.debug("Deleting account...: {}", request); + + if ((request.getAccountId() == null) || (request.getAccountId().isEmpty())) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Missing account id", "", + Cause.MISSING_ACCOUNT_ID); + return; + } + + boolean wasAccountDeleted = accountRepositoryAdditional.deleteAccount(UUID.fromString(request.getAccountId())); + if (wasAccountDeleted) { + logger.info("Account successfully deleted: {}", request.getAccountId()); + responseObserver.onNext(StatusResponse.newBuilder().setStatus("SUCCESS").build()); + responseObserver.onCompleted(); + return; + } + + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Error deleting account with id", + request.getAccountId(), Cause.ERROR_DELETING_ACCOUNT); + return; + } + + @Override + public void deleteProfile(DeleteProfileRequest request, StreamObserver responseObserver) { + logger.debug("Deleting profile: {}", request); + if ((request.getProfileId() == null) || (request.getProfileId().isEmpty())) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Missing profile id", "", + Cause.MISSING_PROFILE_ID); + return; + } + + boolean wasProfileDeleted = accountRepositoryAdditional.deleteProfile(UUID.fromString(request.getProfileId())); + if (wasProfileDeleted) { + logger.info("The profile was deleted successfully."); + responseObserver.onNext(StatusResponse.newBuilder().setStatus("SUCCESS").build()); + responseObserver.onCompleted(); + return; + } + + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Error deleting profile.", "", + Cause.ERROR_DELETING_PROFILE); + return; + } + + @Override + public void addAuthenticationProviderToProfile(AddAuthenticationProviderRequest request, + StreamObserver responseObserver) { + logger.info("Adding authentication provider to profile requested."); + logger.debug("Adding authentication provider to profile requested: {}", request); + if ((request.getProfileId() == null) || (request.getProfileId().isEmpty())) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Missing profile id", "", + Cause.MISSING_PROFILE_ID); + return; + } + if (request.getAuthenticationProvider().getAuthenticationTypeValue() == 0) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Missing auth provider type", + "", Cause.MISSING_AUTH_PROVIDER_TYPE); + return; + } + if (request.getAuthenticationProvider().getAuthenticationProvider() == null + || request.getAuthenticationProvider().getAuthenticationProvider().isEmpty()) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "Missing auth provider identifier", "", Cause.MISSING_AUTH_PROVIDER_IDENTIFIER); + return; + } + Cause cause = validator.validateAddAuthenticationProviderRequest(request); + if (cause != null) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Validation failed", "", + cause); + return; + } + + if (request.getAuthenticationProvider().getAuthenticationType() == AuthenticationType.PHONE) { + // Get the normalized phone number from libphone + AuthProviderDetails newAuthProviderDetails = AuthProviderDetails.newBuilder() + .setAuthenticationType(request.getAuthenticationProvider().getAuthenticationType()) + .setAuthenticationProvider(phoneNumberValidator + .getNormalizedPhoneNumber(request.getAuthenticationProvider().getAuthenticationProvider())) + .build(); + AddAuthenticationProviderRequest newRequest = AddAuthenticationProviderRequest.newBuilder() + .setProfileId(request.getProfileId()).setAuthenticationProvider(newAuthProviderDetails).build(); + request = newRequest; + } + + // Make sure that the requested profile id for update exists in DB. + Profile profile = profileRepository.findByProfileId(UUID.fromString(request.getProfileId())); + if (profile == null) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "Profile id {} misnot found in DB.", request.getProfileId(), Cause.PROFILE_NOT_FOUND); + return; + } + // Make sure that the requested authentication provider is not already used in the system. + ProfileByAuthenticationProvider profileByAuthProvider = profileByAutheticationProviderRepository + .findByAuthenticationProviderAndAuthenticationProviderType( + request.getAuthenticationProvider().getAuthenticationProvider(), + request.getAuthenticationProvider().getAuthenticationType().name()); + if (profileByAuthProvider != null) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "Authentication provider: {} already used.", request.getAuthenticationProvider().toString(), + Cause.AUTH_PROVIDER_ALREADY_USED); + return; + } + boolean result = accountRepositoryAdditional.addAuthenticationProvider(UUID.fromString(request.getProfileId()), + AuthenticationProvider.createAuthenticationProviderFromProto(request.getAuthenticationProvider())); + if (result) { + logger.info("Authentication provider {}:{} successfuly added for profile id {}.", + request.getAuthenticationProvider().getAuthenticationType().name(), + request.getAuthenticationProvider().getAuthenticationProvider(), request.getProfileId()); + responseObserver.onNext(StatusResponse.newBuilder().setStatus("SUCCESS").build()); + responseObserver.onCompleted(); + return; + } + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "Authentication provider was not successfuly added for profile id {}.", request.getProfileId(), + Cause.INTERNAL_SERVER_ERROR); + return; + } + + @Override + public void addContactInfoToAccount(AddContactInfoRequest request, + StreamObserver responseObserver) { + logger.info("Adding contact info to account requested."); + logger.debug("Adding contact info to account requested: {}", request); + Optional> validationResult = validator + .validateContactInfoRequest(request.getAccountId(), request.getContactInfo()); + if (validationResult.isPresent()) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + validationResult.get().getValue(), "", validationResult.get().getKey()); + return; + } + if (request.getContactInfo().getType() == ContactType.PHONE_CONTACT) { + request = phoneNumberNormalizer.normalizePhoneNumber(request); + } + boolean result = accountRepositoryAdditional.addContactInfo(UUID.fromString(request.getAccountId()), + ContactInfo.createContactInfoFromProto(request.getContactInfo())); + if (result) { + logger.info("Contact info {}:{} was added to account {}.", request.getContactInfo().getType().name(), + request.getContactInfo().getValue(), request.getAccountId()); + responseObserver.onNext(StatusResponse.newBuilder().setStatus("SUCCESS").build()); + responseObserver.onCompleted(); + return; + } + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "Contact info was not added to account {}.", request.getAccountId(), Cause.ERROR_ADDING_CONTACT_INFO); + return; + } + + @Override + public void deleteContactInfoFromAccount(DeleteContactInfoRequest request, + StreamObserver responseObserver) { + logger.info("Removing contact info from account requested."); + logger.debug("Removing contact info from account requested: {}", request); + Optional> validationResult = validator + .validateContactInfoRequest(request.getAccountId(), request.getContactInfo()); + if (validationResult.isPresent()) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Validation failed. {}.", + validationResult.get().getRight(), validationResult.get().getLeft()); + return; + } + if (request.getContactInfo().getType() == ContactType.PHONE_CONTACT) { + request = phoneNumberNormalizer.normalizePhoneNumber(request); + } + boolean result = accountRepositoryAdditional.deleteContactInfo(UUID.fromString(request.getAccountId()), + ContactInfo.createContactInfoFromProto(request.getContactInfo())); + if (result) { + logger.info("Contact info {}:{} was removed from account {}.", request.getContactInfo().getType().name(), + request.getContactInfo().getValue(), request.getAccountId()); + responseObserver.onNext(StatusResponse.newBuilder().setStatus("SUCCESS").build()); + responseObserver.onCompleted(); + return; + } + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "Contact info was not removed from account {}.", request.getAccountId(), + Cause.ERROR_REMOVING_CONTACT_INFO); + return; + } + + @Override + public void deleteAuthenticationProviderFromProfile(DeleteAuthenticationProviderRequest request, + StreamObserver responseObserver) { + logger.info("Deleting Authentication Provider from profile: {}", request); + + if ((request.getProfileId() == null) || (request.getProfileId().isEmpty())) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Missing profile id.", "", + Cause.MISSING_PROFILE_ID); + return; + } + if (request.getAuthenticationProvider().getAuthenticationTypeValue() == 0) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Missing auth provider type.", + "", Cause.MISSING_AUTH_PROVIDER_TYPE); + return; + } + if (request.getAuthenticationProvider().getAuthenticationProvider() == null + || request.getAuthenticationProvider().getAuthenticationProvider().isEmpty()) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Missing auth provider id.", + "", Cause.MISSING_AUTH_PROVIDER_IDENTIFIER); + return; + } + + Cause cause = validator.validateDeleteAuthenticationProviderRequest(request); + if (cause != null) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Validation failed", "", + cause); + return; + } + + // Make sure that the requested profile id for update exists in DB. + Profile profile = profileRepository.findByProfileId(UUID.fromString(request.getProfileId())); + if (profile == null) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), "Profile id {} missing in DB.", + request.getProfileId(), Cause.PROFILE_NOT_FOUND); + return; + } + + AuthenticationProvider authenticationProviderToDelete = AuthenticationProvider + .createAuthenticationProviderFromProto(request.getAuthenticationProvider()); + profile.removeAuthenticationProvider(authenticationProviderToDelete); + + if (profile.getAuthenticationProviders().size() < MIN_NUMBER_OF_AUTH_PROVIDERS_IN_PROFILE) { + logger.error( + "Error deleting authentication provider {} from profile with id {}. Check the number of authentication providers.", + request.getAuthenticationProvider(), request.getProfileId()); + responseObserver.onNext(StatusResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(Cause.ERROR_DELETING_AUTH_PROVIDER)).build()); + responseObserver.onCompleted(); + return; + } + + boolean result = accountRepositoryAdditional.deleteAuthenticationProvider(profile, + authenticationProviderToDelete); + + if (result) { + logger.info("Authentication provider {}:{} successfuly deleted from profile id {}.", + request.getAuthenticationProvider().getAuthenticationType().name(), + request.getAuthenticationProvider().getAuthenticationProvider(), request.getProfileId()); + responseObserver.onNext(StatusResponse.newBuilder().setStatus("SUCCESS").build()); + responseObserver.onCompleted(); + return; + } + logger.error("Authentication provider {}:{} was not successfuly deleted from profile id {}.", + request.getAuthenticationProvider().getAuthenticationType().name(), + request.getAuthenticationProvider().getAuthenticationProvider(), request.getProfileId()); + responseObserver.onNext(StatusResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(Cause.INTERNAL_SERVER_ERROR)).build()); + responseObserver.onCompleted(); + return; + } + + @Override + public void editContactInfoForAccount(EditContactInfoRequest request, + StreamObserver responseObserver) { + logger.info("Removing contact info from account requested."); + logger.debug("Removing contact info from account requested: {}", request); + Optional> validationResultEditContactInfo = validator + .validateEditContactInfoRequest(request.getAccountId(), request.getOldContactInfo(), + request.getEditedContactInfo()); + if (validationResultEditContactInfo.isPresent()) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + validationResultEditContactInfo.get().getValue(), "", + validationResultEditContactInfo.get().getKey()); + return; + } + + if (!request.getOldContactInfo().getType().equals(request.getEditedContactInfo().getType())) { + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "Error editing Contact info for account {}. Different types: {} and {}.", request.getAccountId(), + Cause.ERROR_EDITING_CONTACT_INFO); + return; + } + + if (request.getOldContactInfo().getType() == ContactType.PHONE_CONTACT) { + request = phoneNumberNormalizer.normalizePhoneNumbers(request); + } + boolean result = accountRepositoryAdditional.editContactInfo(UUID.fromString(request.getAccountId()), + ContactInfo.createContactInfoFromProto(request.getOldContactInfo()), + ContactInfo.createContactInfoFromProto(request.getEditedContactInfo())); + if (result) { + logger.info("Edited Contact Info {}:{} to {}:{} for account {}.", + request.getOldContactInfo().getType().name(), request.getOldContactInfo().getValue(), + request.getEditedContactInfo().getType().name(), request.getEditedContactInfo().getValue(), + request.getAccountId()); + responseObserver.onNext(StatusResponse.newBuilder().setStatus("SUCCESS").build()); + responseObserver.onCompleted(); + return; + } + logAndBuildGrpcStatusResponse(responseObserver, StatusResponse.newBuilder(), + "Contact info was not edited for account {}.", request.getAccountId(), + Cause.ERROR_EDITING_CONTACT_INFO); + return; + } + + private SearchResultDetails buildSearchResultDetails(String id, ByteBuffer avatar, String firstName, + String lastName) { + SearchResultDetails.Builder searchResultDetails = SearchResultDetails.newBuilder(); + + searchResultDetails.setFirstName(firstName).setLastName(lastName); + if (avatar != null) { + searchResultDetails.setAvatar(com.google.protobuf.ByteString.copyFrom(avatar)); + } + + return searchResultDetails.build(); + } + + private static void logAndBuildGrpcSearchResponse(StreamObserver responseObserver, + SearchResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { + + logger.debug(logMessage, logValue); + responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); + responseObserver.onCompleted(); + } + + private static void logAndBuildGrpcAccountResponse(StreamObserver responseObserver, + AccountResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { + + logger.error(logMessage, logValue); + responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); + responseObserver.onCompleted(); + } + + private static void logAndBuildGrpcAccountsResponse(StreamObserver responseObserver, + AccountsResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { + logger.error(logMessage, logValue); + responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); + responseObserver.onCompleted(); + } + + private static void logAndBuildGrpcProfileResponse(StreamObserver responseObserver, + ProfileResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { + logger.error(logMessage, logValue); + responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); + responseObserver.onCompleted(); + } + + private static void logAndBuildGrpcStatusResponse(StreamObserver responseObserver, + StatusResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { + logger.error(logMessage, logValue); + responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); + responseObserver.onCompleted(); + } + +} diff --git a/src/main/java/biz/nynja/account/services/decomposition/AccountCreator.java b/src/main/java/biz/nynja/account/services/decomposition/AccountCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..645d3bc66b32172f9b60987c6e2e1857ad8989dc --- /dev/null +++ b/src/main/java/biz/nynja/account/services/decomposition/AccountCreator.java @@ -0,0 +1,177 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.services.decomposition; + +import java.time.Instant; +import java.util.UUID; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import biz.nynja.account.components.PendingAccountValidator; +import biz.nynja.account.grpc.AccountDetails; +import biz.nynja.account.grpc.AccountResponse; +import biz.nynja.account.grpc.AuthenticationType; +import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; +import biz.nynja.account.grpc.CreatePendingAccountRequest; +import biz.nynja.account.grpc.CreatePendingAccountResponse; +import biz.nynja.account.grpc.ErrorResponse; +import biz.nynja.account.grpc.ErrorResponse.Cause; +import biz.nynja.account.grpc.Role; +import biz.nynja.account.models.Account; +import biz.nynja.account.models.AuthenticationProvider; +import biz.nynja.account.models.PendingAccount; +import biz.nynja.account.models.PendingAccountByAuthenticationProvider; +import biz.nynja.account.phone.PhoneNumberValidator; +import biz.nynja.account.repositories.AccountRepositoryAdditional; +import biz.nynja.account.repositories.PendingAccountRepository; + +@Service +public class AccountCreator { + + private static final Logger logger = LoggerFactory.getLogger(AccountCreator.class); + + private final PendingAccountRepository pendingAccountRepository; + private final AccountRepositoryAdditional accountRepositoryAdditional; + private final PendingAccountValidator pendingAccountValidator; + private final PhoneNumberValidator phoneNumberValidator; + + public AccountCreator(PendingAccountRepository pendingAccountRepository, + AccountRepositoryAdditional accountRepositoryAdditional, PendingAccountValidator pendingAccountValidator, + PhoneNumberValidator phoneNumberValidator) { + this.pendingAccountRepository = pendingAccountRepository; + this.accountRepositoryAdditional = accountRepositoryAdditional; + this.pendingAccountValidator = pendingAccountValidator; + this.phoneNumberValidator = phoneNumberValidator; + } + + /** + * Retrieves a {@link CreatePendingAccountResponse} based on {@link CreatePendingAccountRequest} + * + * @param request + * @return + */ + public CreatePendingAccountResponse retrieveCreatePendingAccountResponse(CreatePendingAccountRequest request) { + Cause cause = pendingAccountValidator.validateCreatePendingAccountRequest(request); + if (cause != null) { + return CreatePendingAccountResponse.newBuilder().setError(ErrorResponse.newBuilder().setCause(cause)) + .build(); + } + + if (request.getAuthenticationType() == AuthenticationType.PHONE) { + // Get the normalized phone number from libphone + CreatePendingAccountRequest newRequest = CreatePendingAccountRequest.newBuilder() + .setAuthenticationType(request.getAuthenticationType()).setAuthenticationProvider( + phoneNumberValidator.getNormalizedPhoneNumber(request.getAuthenticationProvider())) + .build(); + request = newRequest; + } + + CreatePendingAccountResponse response = updateAndValidatePendingAccountCreation(request); + if (response != null) { + return response; + } + + PendingAccount pendingAccount = PendingAccount.fromProto(request); + + pendingAccount.setAccountId(UUID.randomUUID()); + pendingAccount.setProfileId(UUID.randomUUID()); + pendingAccount.setCreationTimestamp(Instant.now().toEpochMilli()); + + PendingAccount savedPendingAccount = pendingAccountRepository.save(pendingAccount); + logger.debug("Pending account \"{}\" saved into the DB", savedPendingAccount); + response = CreatePendingAccountResponse.newBuilder().setPendingAccountDetails(savedPendingAccount.toProto()) + .build(); + logger.info("Pending account created successfully."); + logger.debug("Pending account: \"{}\" created successfully.", response); + return response; + } + + private CreatePendingAccountResponse updateAndValidatePendingAccountCreation(CreatePendingAccountRequest request) { + PendingAccountByAuthenticationProvider foundExistingPendingAccount = accountRepositoryAdditional + .findSameAuthenticationProviderInPendingAccount( + AuthenticationProvider.createAuthenticationProviderFromStrings( + request.getAuthenticationType().name(), request.getAuthenticationProvider())); + + PendingAccount updatedPendingAccount = new PendingAccount(); + + if (foundExistingPendingAccount != null) { + updatedPendingAccount.setAccountId(foundExistingPendingAccount.getAccountId()); + updatedPendingAccount.setProfileId(foundExistingPendingAccount.getProfileId()); + updatedPendingAccount.setAuthenticationProvider(foundExistingPendingAccount.getAuthenticationProvider()); + updatedPendingAccount + .setAuthenticationProviderType(foundExistingPendingAccount.getAuthenticationProviderType()); + updatedPendingAccount.setCreationTimestamp(Instant.now().toEpochMilli()); + + PendingAccount updatePendingAccount = pendingAccountRepository.save(updatedPendingAccount); + return CreatePendingAccountResponse.newBuilder().setPendingAccountDetails(updatePendingAccount.toProto()) + .build(); + } + + if (accountRepositoryAdditional.authenticationProviderAlreadyUsedInAccount( + AuthenticationProvider.createAuthenticationProviderFromStrings(request.getAuthenticationType().name(), + request.getAuthenticationProvider()))) { + return logAndBuildGrpcPendingAccountResponse(CreatePendingAccountResponse.newBuilder(), + "Account already created", "", Cause.ACCOUNT_ALREADY_CREATED); + } + return null; + } + + /** + * Retrieves a {@link AccountResponse} based on a {@link CompletePendingAccountCreationRequest} + * + * @param request + * @return + */ + public AccountResponse retrieveCompletePendingAccountResponse(CompletePendingAccountCreationRequest request) { + AccountResponse response = validateCompletePendingAccountCreationRequest(request); + if (response != null) { + return response; + } + + if (request.getRolesList() == null || request.getRolesList().isEmpty()) { + request = CompletePendingAccountCreationRequest.newBuilder(request).addRoles(Role.USER).build(); + } + + Account createdAccount = accountRepositoryAdditional.completePendingAccountCreation(request); + + if (createdAccount == null) { + return logAndBuildGrpcAccountResponse(AccountResponse.newBuilder(), + "Error creating account with useraname: {}", request.getUsername(), Cause.ERROR_CREATING_ACCOUNT); + } else { + logger.debug("Account \"{}\" saved into the DB", createdAccount); + AccountDetails details = createdAccount.toProto(); + logger.debug("Account: \"{}\" created successfully.", response); + return AccountResponse.newBuilder().setAccountDetails(details).build(); + } + } + + private AccountResponse validateCompletePendingAccountCreationRequest( + CompletePendingAccountCreationRequest request) { + Cause cause = pendingAccountValidator.validateCompletePendingAccountCreationRequest(request); + if (cause != null) { + return logAndBuildGrpcAccountResponse(AccountResponse.newBuilder(), "Validation failed", "", cause); + } + + if (request.getUsername() != null && !request.getUsername().trim().isEmpty() && accountRepositoryAdditional + .foundExistingNotOwnUsername(UUID.fromString(request.getAccountId()), request.getUsername())) { + return logAndBuildGrpcAccountResponse(AccountResponse.newBuilder(), "User name already in use: {}", + request.getUsername(), Cause.USERNAME_ALREADY_USED); + } + return null; + } + + private static CreatePendingAccountResponse logAndBuildGrpcPendingAccountResponse( + CreatePendingAccountResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { + logger.error(logMessage, logValue); + return newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build(); + } + + private static AccountResponse logAndBuildGrpcAccountResponse(AccountResponse.Builder newBuilder, String logMessage, + String logValue, Cause cause) { + logger.error(logMessage, logValue); + return newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build(); + } +} diff --git a/src/main/java/biz/nynja/account/services/decomposition/AccountProvider.java b/src/main/java/biz/nynja/account/services/decomposition/AccountProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..1126e4a710bde49afd2915dea9109345da6fe631 --- /dev/null +++ b/src/main/java/biz/nynja/account/services/decomposition/AccountProvider.java @@ -0,0 +1,142 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.services.decomposition; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import biz.nynja.account.components.AccountServiceHelper; +import biz.nynja.account.grpc.AccountByAccountIdRequest; +import biz.nynja.account.grpc.AccountByAuthenticationProviderRequest; +import biz.nynja.account.grpc.AccountDetails; +import biz.nynja.account.grpc.AccountResponse; +import biz.nynja.account.grpc.AccountsByProfileIdRequest; +import biz.nynja.account.grpc.AccountsList; +import biz.nynja.account.grpc.AccountsResponse; +import biz.nynja.account.grpc.AuthenticationType; +import biz.nynja.account.models.Account; +import biz.nynja.account.models.AccountByProfileId; +import biz.nynja.account.models.AccountByQrCode; +import biz.nynja.account.models.AccountByUsername; +import biz.nynja.account.phone.PhoneNumberValidator; +import biz.nynja.account.repositories.AccountByProfileIdRepository; +import biz.nynja.account.repositories.AccountByQrCodeRepository; +import biz.nynja.account.repositories.AccountByUsernameRepository; +import biz.nynja.account.repositories.AccountRepository; +import biz.nynja.account.services.AccountServiceImpl; + +@Service +public class AccountProvider { + + private static final Logger logger = LoggerFactory.getLogger(AccountServiceImpl.class); + + private final AccountRepository accountRepository; + private final AccountByProfileIdRepository accountByProfileIdRepository; + private final AccountByUsernameRepository accountByUsernameRepository; + private final AccountByQrCodeRepository accountByQrCodeRepository; + private final PhoneNumberValidator validator; + private final AccountServiceHelper accountServiceHelper; + + public AccountProvider(AccountRepository accountRepository, + AccountByProfileIdRepository accountByProfileIdRepository, + AccountByUsernameRepository accountByUsernameRepository, + AccountByQrCodeRepository accountByQrCodeRepository, PhoneNumberValidator validator, + AccountServiceHelper accountServiceHelper) { + this.accountRepository = accountRepository; + this.accountByProfileIdRepository = accountByProfileIdRepository; + this.validator = validator; + this.accountServiceHelper = accountServiceHelper; + this.accountByUsernameRepository = accountByUsernameRepository; + this.accountByQrCodeRepository = accountByQrCodeRepository; + } + + public Optional getAccountByAuthenticationProvider(AuthenticationType type, + String authenticationIdentifier) { + + if (type == AuthenticationType.PHONE) { + authenticationIdentifier = validator.getNormalizedPhoneNumber(authenticationIdentifier); + } + Account account = accountServiceHelper.getAccountByAuthenticationProviderHelper(authenticationIdentifier, + type.toString()); + if (account == null) { + logger.debug("No matching accounts found for authetntication provider {}: {}", type, + authenticationIdentifier); + return Optional.empty(); + } + + return Optional.of(account); + } + + public Optional getAccountResponseByUsername(String username) { + AccountByUsername account = accountByUsernameRepository.findByUsername(username); + if (account == null) { + return Optional.empty(); + } + AccountResponse response = AccountResponse.newBuilder().setAccountDetails(account.toProto()).build(); + logger.debug("Found result for account by username {}:", username, response); + return Optional.of(response); + } + + public Optional getAccountResponseByQrCode(String qrCode) { + AccountByQrCode account = accountByQrCodeRepository.findByQrCode(qrCode); + if (account == null) { + return Optional.empty(); + } + AccountResponse response = AccountResponse.newBuilder().setAccountDetails(account.toProto()).build(); + logger.debug("Found result for account by username {}:", qrCode, response); + return Optional.of(response); + } + + public Optional getAccountResponseByAuthenticationProvider(AuthenticationType type, + String authenticationIdentifier) { + Optional account = getAccountByAuthenticationProvider(type, authenticationIdentifier); + if (!account.isPresent()) { + return Optional.empty(); + } + + AccountResponse response = AccountResponse.newBuilder().setAccountDetails(account.get().toProto()).build(); + logger.debug("Found result for account by authentication provider {}: {}: \"{}\"", type, + authenticationIdentifier, response); + return Optional.of(response); + } + + public Optional getAllAccountsByProfileId(AccountsByProfileIdRequest request) { + List listAccountsByProfileId = accountByProfileIdRepository + .findAllByProfileId(UUID.fromString(request.getProfileId())); + if (listAccountsByProfileId.size() == 0) { + return Optional.empty(); + } + List responseList = listAccountsByProfileId.stream().map(AccountByProfileId::toProto) + .collect(Collectors.toList()); + AccountsResponse response = AccountsResponse.newBuilder() + .setAccountsResponse(AccountsList.newBuilder().addAllAccountDetails(responseList)).build(); + logger.debug("Returned response: \"{}\".", response); + return Optional.of(response); + } + + public Optional getAccountByAccountId(AccountByAccountIdRequest request) { + logger.info("Getting accounts by account id: {}", request.getAccountId()); + Account account = accountRepository.findByAccountId(UUID.fromString(request.getAccountId())); + if (account == null || account.getAccountId() == null) { + return Optional.empty(); + } + AccountResponse response = AccountResponse.newBuilder().setAccountDetails(account.toProto()).build(); + logger.debug("Returned response: \"{}\".", response); + return Optional.of(response); + } + + private AccountByAuthenticationProviderRequest normalizedPhoneNumber( + AccountByAuthenticationProviderRequest request) { + return AccountByAuthenticationProviderRequest.newBuilder() + .setAuthenticationType(request.getAuthenticationType()) + .setAuthenticationIdentifier(validator.getNormalizedPhoneNumber(request.getAuthenticationIdentifier())) + .build(); + } +} diff --git a/src/main/java/biz/nynja/account/validation/Validation.java b/src/main/java/biz/nynja/account/validation/Validation.java new file mode 100644 index 0000000000000000000000000000000000000000..9ae0982ccd05c540755579adbc1039bcf38daed4 --- /dev/null +++ b/src/main/java/biz/nynja/account/validation/Validation.java @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.validation; + +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +import biz.nynja.account.grpc.ErrorResponse.Cause; + +public class Validation { + List errors = new LinkedList<>(); + + public Validation() { + } + + public Validation(ValidationError e) { + errors.add(e); + } + + public void addError(ValidationError e) { + errors.add(e); + } + + public boolean hasErrors() { + return errors.size() > 0; + } + + public List getErrors() { + return errors; + } + + public String getErrorMessage() { + if (!hasErrors()) { + return ""; + } + StringBuilder builder = new StringBuilder(); + for (ValidationError error : errors) { + builder.append("Message: ").append(error.getMessage()).append(". "); + if (error.getCause() != null) { + builder.append("Cause: ").append(error.getCause()); + } + } + + return builder.toString(); + } + + public Optional getCause() { + if (!hasErrors()) { + return Optional.empty(); + } + + if (errors.size() == 1) { + return Optional.of(errors.get(0).getCause()); + } else { + return Optional.of(Cause.MULTIPLE_INVALID_PARAMETERS); + } + + } + +} diff --git a/src/main/java/biz/nynja/account/validation/ValidationError.java b/src/main/java/biz/nynja/account/validation/ValidationError.java new file mode 100644 index 0000000000000000000000000000000000000000..f9bcaae33f076ad62329386bd1ff4945d7c54e1b --- /dev/null +++ b/src/main/java/biz/nynja/account/validation/ValidationError.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.validation; + +import biz.nynja.account.grpc.ErrorResponse.Cause; + +public class ValidationError { + private String message; + private Cause cause; + + public ValidationError(String message, Cause cause) { + this.setMessage(message); + this.setCause(cause); + } + + public ValidationError(String message) { + this.setMessage(message); + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Cause getCause() { + return cause; + } + + public void setCause(Cause cause) { + this.cause = cause; + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 5e70478f6158735260780bbe4ad5fe0d15e8c7cf..83c05a166aa6e841497750795f8130382a7700de 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -2,10 +2,34 @@ grpc: enabled: true enableReflection: false port: 6565 - + unitTest: + port: + accountServiceTest: 11001 + spring: data: cassandra: keyspace-name: account contact-points: localhost - port: 9042 \ No newline at end of file + port: 9042 + +complete: + pending: + account: + timeout-minutes: 30 + +#Metrics related configurations +management: + endpoint: + metrics: + enabled: true + prometheus: + enabled: true + endpoints: + web: + exposure: + include: 'prometheus, health, info' + metrics: + export: + prometheus: + enabled: true \ No newline at end of file diff --git a/src/main/resources/application-production.yml b/src/main/resources/application-production.yml index 89f53d3fb6e9d70f6ef431e68c32992750d36517..4d7051cad6dc00478043b482af086b27d54f826b 100644 --- a/src/main/resources/application-production.yml +++ b/src/main/resources/application-production.yml @@ -2,10 +2,34 @@ grpc: enabled: ${GRPC_ENABLED:true} enableReflection: ${GRPC_ENABLE_REFLECTION:false} port: ${GRPC_SERVER_PORT:6565} + unitTest: + port: + accountServiceTest: 11001 spring: data: cassandra: keyspace-name: ${CASSANDRA_KEYSPACE:account} - contact-points: ${CASSANDRA_CONTACT_POINTS:localhost} - port: ${CASSANDRA_PORT:9042} \ No newline at end of file + contact-points: ${CASSANDRA_CONTACT_POINTS:cassandra.cassandra.svc.cluster.local} + port: ${CASSANDRA_PORT:9042} + +complete: + pending: + account: + timeout-minutes: ${COMPLETE_PENDING_ACCOUNT_TIMEOUT_MINUTES:30} + +#Metrics related configurations +management: + endpoint: + metrics: + enabled: true + prometheus: + enabled: true + endpoints: + web: + exposure: + include: 'prometheus, health, info' + metrics: + export: + prometheus: + enabled: true diff --git a/src/main/resources/countries.txt b/src/main/resources/countries.txt new file mode 100644 index 0000000000000000000000000000000000000000..60d177a7e180abf4ee8ccd0bb19268187e2f0cf7 --- /dev/null +++ b/src/main/resources/countries.txt @@ -0,0 +1,236 @@ +1876;JM;Jamaica;XXX XXXX +1869;KN;Saint Kitts & Nevis;XXX XXXX +1868;TT;Trinidad & Tobago;XXX XXXX +1784;VC;Saint Vincent & the Grenadines;XXX XXXX +1767;DM;Dominica;XXX XXXX +1758;LC;Saint Lucia;XXX XXXX +1721;SX;Sint Maarten;XXX XXXX +1684;AS;American Samoa;XXX XXXX +1671;GU;Guam;XXX XXXX +1670;MP;Northern Mariana Islands;XXX XXXX +1664;MS;Montserrat;XXX XXXX +1649;TC;Turks & Caicos Islands;XXX XXXX +1473;GD;Grenada;XXX XXXX +1441;BM;Bermuda;XXX XXXX +1345;KY;Cayman Islands;XXX XXXX +1340;VI;US Virgin Islands;XXX XXXX +1284;VG;British Virgin Islands;XXX XXXX +1268;AG;Antigua & Barbuda;XXX XXXX +1264;AI;Anguilla;XXX XXXX +1246;BB;Barbados;XXX XXXX +1242;BS;Bahamas;XXX XXXX +998;UZ;Uzbekistan;XX XXXXXXX +996;KG;Kyrgyzstan;XXX XXXXXX +995;GE;Georgia;XXX XXX XXX +994;AZ;Azerbaijan;XX XXX XXXX +993;TM;Turkmenistan;XX XXXXXX +992;TJ;Tajikistan;XX XXX XXXX +977;NP;Nepal;XX XXXX XXXX +976;MN;Mongolia;XX XX XXXX +975;BT;Bhutan;XX XXX XXX +974;QA;Qatar;XX XXX XXX +973;BH;Bahrain;XXXX XXXX +972;IL;Israel;XX XXX XXXX +971;AE;United Arab Emirates;XX XXX XXXX +970;PS;Palestine;XXX XX XXXX +968;OM;Oman;XXXX XXXX +967;YE;Yemen;XXX XXX XXX +966;SA;Saudi Arabia;XX XXX XXXX +965;KW;Kuwait;XXXX XXXX +964;IQ;Iraq;XXX XXX XXXX +963;SY;Syria;XXX XXX XXX +962;JO;Jordan;X XXXX XXXX +961;LB;Lebanon +960;MV;Maldives;XXX XXXX +886;TW;Taiwan;XXX XXX XXX +883;GO;International Networks +882;GO;International Networks +881;GO;Global Mobile Satellite +880;BD;Bangladesh +856;LA;Laos;XX XX XXX XXX +855;KH;Cambodia +853;MO;Macau;XXXX XXXX +852;HK;Hong Kong;X XXX XXXX +850;KP;North Korea +692;MH;Marshall Islands +691;FM;Micronesia +690;TK;Tokelau +689;PF;French Polynesia +688;TV;Tuvalu +687;NC;New Caledonia +686;KI;Kiribati +685;WS;Samoa +683;NU;Niue +682;CK;Cook Islands +681;WF;Wallis & Futuna +680;PW;Palau +679;FJ;Fiji +678;VU;Vanuatu +677;SB;Solomon Islands +676;TO;Tonga +675;PG;Papua New Guinea +674;NR;Nauru +673;BN;Brunei Darussalam;XXX XXXX +672;NF;Norfolk Island +670;TL;Timor-Leste +599;BQ;Bonaire, Sint Eustatius & Saba +599;CW;Curaçao +598;UY;Uruguay;X XXX XXXX +597;SR;Suriname;XXX XXXX +596;MQ;Martinique +595;PY;Paraguay;XXX XXX XXX +594;GF;French Guiana +593;EC;Ecuador;XX XXX XXXX +592;GY;Guyana +591;BO;Bolivia;X XXX XXXX +590;GP;Guadeloupe;XXX XX XX XX +509;HT;Haiti +508;PM;Saint Pierre & Miquelon +507;PA;Panama;XXXX XXXX +506;CR;Costa Rica;XXXX XXXX +505;NI;Nicaragua;XXXX XXXX +504;HN;Honduras;XXXX XXXX +503;SV;El Salvador;XXXX XXXX +502;GT;Guatemala;X XXX XXXX +501;BZ;Belize +500;FK;Falkland Islands +423;LI;Liechtenstein +421;SK;Slovakia;XXX XXX XXX +420;CZ;Czech Republic;XXX XXX XXX +389;MK;Macedonia;XX XXX XXX +387;BA;Bosnia & Herzegovina;XX XXX XXX +386;SI;Slovenia;XX XXX XXX +385;HR;Croatia +383;XK;Kosovo;XXXX XXXX +382;ME;Montenegro +381;RS;Serbia;XX XXX XXXX +380;UA;Ukraine;XX XXX XX XX +378;SM;San Marino;XXX XXX XXXX +377;MC;Monaco;XXXX XXXX +376;AD;Andorra;XX XX XX +375;BY;Belarus;XX XXX XXXX +374;AM;Armenia;XX XXX XXX +373;MD;Moldova;XX XXX XXX +372;EE;Estonia +371;LV;Latvia;XXX XXXXX +370;LT;Lithuania;XXX XXXXX +359;BG;Bulgaria +358;FI;Finland +357;CY;Cyprus;XXXX XXXX +356;MT;Malta;XX XX XX XX +355;AL;Albania;XX XXX XXXX +354;IS;Iceland;XXX XXXX +353;IE;Ireland;XX XXX XXXX +352;LU;Luxembourg +351;PT;Portugal;X XXXX XXXX +350;GI;Gibraltar;XXXX XXXX +299;GL;Greenland;XXX XXX +298;FO;Faroe Islands;XXX XXX +297;AW;Aruba;XXX XXXX +291;ER;Eritrea;X XXX XXX +290;SH;Saint Helena;XX XXX +269;KM;Comoros;XXX XXXX +268;SZ;Swaziland;XXXX XXXX +267;BW;Botswana;XX XXX XXX +266;LS;Lesotho;XX XXX XXX +265;MW;Malawi;77 XXX XXXX +264;NA;Namibia;XX XXX XXXX +263;ZW;Zimbabwe;XX XXX XXXX +262;RE;Réunion;XXX XXX XXX +261;MG;Madagascar;XX XX XXX XX +260;ZM;Zambia;XX XXX XXXX +258;MZ;Mozambique;XX XXX XXXX +257;BI;Burundi;XX XX XXXX +256;UG;Uganda;XX XXX XXXX +255;TZ;Tanzania;XX XXX XXXX +254;KE;Kenya;XXX XXX XXX +253;DJ;Djibouti;XX XX XX XX +252;SO;Somalia;XX XXX XXX +251;ET;Ethiopia;XX XXX XXXX +250;RW;Rwanda;XXX XXX XXX +249;SD;Sudan;XX XXX XXXX +248;SC;Seychelles;X XX XX XX +247;SH;Saint Helena;XXXX +246;IO;Diego Garcia;XXX XXXX +245;GW;Guinea-Bissau;XXX XXXX +244;AO;Angola;XXX XXX XXX +243;CD;Congo (Dem. Rep.);XX XXX XXXX +242;CG;Congo (Rep.);XX XXX XXXX +241;GA;Gabon;X XX XX XX +240;GQ;Equatorial Guinea;XXX XXX XXX +239;ST;São Tomé & Príncipe;XX XXXXX +238;CV;Cape Verde;XXX XXXX +237;CM;Cameroon;XXXX XXXX +236;CF;Central African Rep.;XX XX XX XX +235;TD;Chad;XX XX XX XX +234;NG;Nigeria +233;GH;Ghana +232;SL;Sierra Leone;XX XXX XXX +231;LR;Liberia +230;MU;Mauritius +229;BJ;Benin;XX XXX XXX +228;TG;Togo;XX XXX XXX +227;NE;Niger;XX XX XX XX +226;BF;Burkina Faso;XX XX XX XX +225;CI;Côte d`Ivoire;XX XXX XXX +224;GN;Guinea;XXX XXX XXX +223;ML;Mali;XXXX XXXX +222;MR;Mauritania;XXXX XXXX +221;SN;Senegal;XX XXX XXXX +220;GM;Gambia;XXX XXXX +218;LY;Libya;XX XXX XXXX +216;TN;Tunisia;XX XXX XXX +213;DZ;Algeria;XXX XX XX XX +212;MA;Morocco;XX XXX XXXX +211;SS;South Sudan;XX XXX XXXX +98;IR;Iran;XXX XXX XXXX +95;MM;Myanmar +94;LK;Sri Lanka;XX XXX XXXX +93;AF;Afghanistan;XXX XXX XXX +92;PK;Pakistan;XXX XXX XXXX +91;IN;India;XXXXX XXXXX +90;TR;Turkey;XXX XXX XXXX +86;CN;China;XXX XXXX XXXX +84;VN;Vietnam +82;KR;South Korea +81;JP;Japan;XX XXXX XXXX +66;TH;Thailand;X XXXX XXXX +65;SG;Singapore;XXXX XXXX +64;NZ;New Zealand +63;PH;Philippines;XXX XXX XXXX +62;ID;Indonesia +61;AU;Australia;XXX XXX XXX +60;MY;Malaysia +58;VE;Venezuela;XXX XXX XXXX +57;CO;Colombia;XXX XXX XXXX +56;CL;Chile;X XXXX XXXX +55;BR;Brazil;XX XXXXX XXXX +54;AR;Argentina +53;CU;Cuba;XXXX XXXX +52;MX;Mexico +51;PE;Peru;XXX XXX XXX +49;DE;Germany +48;PL;Poland;XX XXX XXXX +47;NO;Norway;XXXX XXXX +46;SE;Sweden;XX XXX XXXX +45;DK;Denmark;XXXX XXXX +44;GB;United Kingdom;XXXX XXXXXX +43;AT;Austria +42;YL;Y-land +41;CH;Switzerland;XX XXX XXXX +40;RO;Romania;XXX XXX XXX +39;IT;Italy +36;HU;Hungary;XXX XXX XXX +34;ES;Spain;XXX XXX XXX +33;FR;France;X XX XX XX XX +32;BE;Belgium;XXX XX XX XX +31;NL;Netherlands;X XX XX XX XX +30;GR;Greece;XXX XXX XXXX +27;ZA;South Africa;XX XXX XXXX +20;EG;Egypt;XX XXXX XXXX +7;KZ;Kazakhstan;XXX XXX XX XX +7;RU;Russian Federation;XXX XXX XXXX +1;PR;Puerto Rico;XXX XXX XXXX +1;DO;Dominican Rep.;XXX XXX XXXX +1;CA;Canada;XXX XXX XXXX +1;US;USA;XXX XXX XXXX \ No newline at end of file diff --git a/src/main/resources/logback-spring.groovy b/src/main/resources/logback-spring.groovy new file mode 100644 index 0000000000000000000000000000000000000000..417c6c7f7fb5621ccd58d76f3f1193ee771e7e41 --- /dev/null +++ b/src/main/resources/logback-spring.groovy @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ + +import ch.qos.logback.classic.encoder.PatternLayoutEncoder +import ch.qos.logback.core.ConsoleAppender +import ch.qos.logback.core.rolling.RollingFileAppender +import ch.qos.logback.core.rolling.TimeBasedRollingPolicy +import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP +import ch.qos.logback.core.status.OnConsoleStatusListener +import ch.qos.logback.classic.Level + +statusListener(OnConsoleStatusListener) + +def file = "${System.getProperty('log.dir', '')}account-service-%d.%i.log" + +appender("FILE", RollingFileAppender) { + // add a status message regarding the file property + addInfo("Starting logging to $file") + append = true + encoder(PatternLayoutEncoder) { pattern = "%d{HH:mm:ss.SSS} %level %logger - %msg%n" } + rollingPolicy(TimeBasedRollingPolicy) { + fileNamePattern = "$file" + timeBasedFileNamingAndTriggeringPolicy(SizeAndTimeBasedFNATP) { maxFileSize = "10mb" }} +} + +appender("Console-Appender", ConsoleAppender) { + encoder(PatternLayoutEncoder) { pattern = "%d{HH:mm:ss.SSS} %level %logger - %msg%n" } +} + +logger("org.springframework.cloud.config.client.ConfigServicePropertySourceLocator", Level.WARN) +root(INFO, ["Console-Appender"]) +root(Level.toLevel("${System.getProperty('log.level', 'INFO')}"), ["FILE"]) \ No newline at end of file diff --git a/src/test/java/biz/nynja/account/ApplicationTests.java b/src/test/java/biz/nynja/account/ApplicationTests.java new file mode 100644 index 0000000000000000000000000000000000000000..45f00f12d7644ce66e8fca486d2ad1949ba23dfb --- /dev/null +++ b/src/test/java/biz/nynja/account/ApplicationTests.java @@ -0,0 +1,24 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ + +package biz.nynja.account; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Main unit test. + */ + +@RunWith(SpringRunner.class) +@ContextConfiguration +public class ApplicationTests { + + @Test + public void testContext() { + } + +} diff --git a/src/test/java/biz/nynja/account/components/AccountServiceHelperTests.java b/src/test/java/biz/nynja/account/components/AccountServiceHelperTests.java new file mode 100644 index 0000000000000000000000000000000000000000..96150641bf99eb6d5e8dd93a923faf80902c670d --- /dev/null +++ b/src/test/java/biz/nynja/account/components/AccountServiceHelperTests.java @@ -0,0 +1,92 @@ +package biz.nynja.account.components; + +import static org.junit.Assert.*; +import static org.mockito.BDDMockito.given; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import biz.nynja.account.components.AccountServiceHelper; +import biz.nynja.account.configurations.CassandraTestsConfig; +import biz.nynja.account.models.Account; +import biz.nynja.account.models.AccountByAuthenticationProvider; +import biz.nynja.account.repositories.AccountByAuthenticationProviderRepository; +import biz.nynja.account.utils.Util; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = { Util.class, CassandraTestsConfig.class }, + webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { + "grpc.port=${grpc.unitTest.port.accountServiceTest}", + "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration", + "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration" }) +@ActiveProfiles("dev") +public class AccountServiceHelperTests { + + @MockBean + private PreparedStatementsCache preparedStatementsCache; + + @MockBean + private AccountByAuthenticationProviderRepository accountByAuthenticationProviderRepository; + + @Autowired + private AccountServiceHelper helper; + + @Autowired + @Qualifier("accountByPhone") + private AccountByAuthenticationProvider accountByPhone; + + @Autowired + @Qualifier("accountByEmail") + private AccountByAuthenticationProvider accountByEmail; + + @Autowired + @Qualifier("accountByFacebook") + private AccountByAuthenticationProvider accountByFacebook; + + @Test + public void testGetAccountByAuthenticationProviderHelperMultipleRecords() { + List accList = new ArrayList<>(); + accList.add(accountByEmail); + accList.add(accountByFacebook); + given(accountByAuthenticationProviderRepository.findAllByAuthenticationProvider(Util.EMAIL)) + .willReturn(accList); + + Account account = helper.getAccountByAuthenticationProviderHelper(Util.EMAIL, "EMAIL"); + assertEquals(Util.EMAIL, account.getAuthenticationProvider()); + } + + @Test + public void testGetAccountByAuthProviderPhoneByPhoneOK() { + List accList = new ArrayList<>(); + accList.add(accountByPhone); + given(accountByAuthenticationProviderRepository.findAllByAuthenticationProvider(Util.PHONE_NUMBER)) + .willReturn(accList); + + Account account = helper.getAccountByAuthenticationProviderHelper(Util.PHONE_NUMBER, "PHONE"); + assertEquals(Util.PHONE_NUMBER, account.getAuthenticationProvider()); + } + + @Test + public void testGetAccountByAuthenticationProviderHelperAccountNotFound() { + List accList = new ArrayList<>(); + accList.add(accountByFacebook); + accList.add(accountByPhone); + given(accountByAuthenticationProviderRepository.findAllByAuthenticationProvider(Util.EMAIL)) + .willReturn(new ArrayList()); + + Account account = helper.getAccountByAuthenticationProviderHelper(Util.EMAIL, "EMAIL"); + assertNull(account); + } + +} diff --git a/src/test/java/biz/nynja/account/components/LibphoneNormalizationParameterizedTest.java b/src/test/java/biz/nynja/account/components/LibphoneNormalizationParameterizedTest.java new file mode 100644 index 0000000000000000000000000000000000000000..79a0e6f5d629a79c5226f19f43d53acdc085544e --- /dev/null +++ b/src/test/java/biz/nynja/account/components/LibphoneNormalizationParameterizedTest.java @@ -0,0 +1,71 @@ +package biz.nynja.account.components; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.Collection; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Configurable; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestContextManager; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit4.rules.SpringClassRule; +import org.springframework.test.context.junit4.rules.SpringMethodRule; + +import biz.nynja.account.phone.PhoneNumberValidator; +import biz.nynja.account.repositories.AccountRepository; +import biz.nynja.account.services.AccountServiceImpl; + +// ================================================================ +// This test is not part of the build but we consider it useful +// and would like to keep it for future use. +// =============================================================== + +@RunWith(Parameterized.class) +@TestExecutionListeners({}) +@ContextConfiguration(classes = { Validator.class }) +public class LibphoneNormalizationParameterizedTest { + + private PhoneNumberValidator validator = new PhoneNumberValidator(); + + private String expectedPhoneNumber; + private String inputPhoneNumber; + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(); + + public LibphoneNormalizationParameterizedTest(String expectedPhoneNumber, String inputPhoneNumber) { + this.expectedPhoneNumber = expectedPhoneNumber; + this.inputPhoneNumber = inputPhoneNumber; + } + + @Parameterized.Parameters + public static Collection phoneNumbers() { + return Arrays.asList(new Object[][] { + { "359887345234", "BG:+359887345234" }, + { "359887345234", "BG:00359887345234" }, + { "359887345234", "BG:359 887 345 234" }, + { "359887345234", "BG:359-887-345-234" } }); + } + + @Test + public void testNormalizedPhoneNumber() { + System.out.println("Input Phone Number is : " + inputPhoneNumber); + + String normalized = validator.getNormalizedPhoneNumber(inputPhoneNumber); + System.out.println("Normalized Phone Number is : " + normalized); + assertEquals(expectedPhoneNumber, normalized); + } +} diff --git a/src/test/java/biz/nynja/account/components/PhoneNumberValidatorTests.java b/src/test/java/biz/nynja/account/components/PhoneNumberValidatorTests.java new file mode 100644 index 0000000000000000000000000000000000000000..1536aaefe8747c25bef3114ec1615dabe09cf2aa --- /dev/null +++ b/src/test/java/biz/nynja/account/components/PhoneNumberValidatorTests.java @@ -0,0 +1,126 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ + +package biz.nynja.account.components; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import biz.nynja.account.phone.PhoneNumberValidator; +import biz.nynja.account.repositories.AccountRepositoryAdditional; + +/** + * Components unit tests. + */ + +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = { PhoneNumberValidator.class }) +public class PhoneNumberValidatorTests { + + @Autowired + @Qualifier("phoneNumberValidator") + private PhoneNumberValidator phoneNumberValidator; + + @MockBean + private AccountRepositoryAdditional accountRepositoryAdditional; + + @Test + public void validPhoneNumberTest() { + + String phoneNumber = "+359887434646"; + String countryCode = "BG"; + boolean isValid = phoneNumberValidator.isPhoneNumberValid(phoneNumber, countryCode); + assertTrue(String.format("Phone number: '%s' should be valid for country '%s'", phoneNumber, countryCode), + isValid == true); + + } + + @Test + public void invalidPhoneNumberTest() { + + String phoneNumber = "+357887434646"; + String countryCode = "BG"; + boolean isValid = phoneNumberValidator.isPhoneNumberValid(phoneNumber, countryCode); + assertFalse(String.format("Phone number: '%s' should be invalid for country '%s'", phoneNumber, countryCode), + isValid == true); + + } + + @Test + public void validNormalizedPhoneNumberTest1() { + + String expectedPhoneNumber = "359887345234"; + String inputPhoneNumber = "BG:+359887345234"; + String normalizedPhoneNumber = phoneNumberValidator.getNormalizedPhoneNumber(inputPhoneNumber); + assertEquals( + String.format("Phone number: '%s' should be normalized to '%s'", inputPhoneNumber, expectedPhoneNumber), + expectedPhoneNumber, normalizedPhoneNumber); + } + + @Test + public void validNormalizedPhoneNumberTest2() { + + String expectedPhoneNumber = "359887345234"; + String inputPhoneNumber = "BG:00359887345234"; + String normalizedPhoneNumber = phoneNumberValidator.getNormalizedPhoneNumber(inputPhoneNumber); + assertEquals( + String.format("Phone number: '%s' should be normalized to '%s'", inputPhoneNumber, expectedPhoneNumber), + expectedPhoneNumber, normalizedPhoneNumber); + } + + @Test + public void validNormalizedPhoneNumberTest3() { + + String expectedPhoneNumber = "359887345234"; + String inputPhoneNumber = "BG:359-887-345-234"; + String normalizedPhoneNumber = phoneNumberValidator.getNormalizedPhoneNumber(inputPhoneNumber); + assertEquals( + String.format("Phone number: '%s' should be normalized to '%s'", inputPhoneNumber, expectedPhoneNumber), + expectedPhoneNumber, normalizedPhoneNumber); + } + + @Test + public void validNormalizedPhoneNumberTest4() { + + String expectedPhoneNumber = "359887345234"; + String inputPhoneNumber = "BG:359 887 345 234"; + String normalizedPhoneNumber = phoneNumberValidator.getNormalizedPhoneNumber(inputPhoneNumber); + assertEquals( + String.format("Phone number: '%s' should be normalized to '%s'", inputPhoneNumber, expectedPhoneNumber), + expectedPhoneNumber, normalizedPhoneNumber); + } + + @Test + public void invalidNormalizedPhoneNumberTest1() { + + String expectedPhoneNumber = "359887345234"; + String inputPhoneNumber = "BG:359887345234567"; + String normalizedPhoneNumber = phoneNumberValidator.getNormalizedPhoneNumber(inputPhoneNumber); + assertNotEquals( + String.format("Phone number: '%s' should be normalized to '%s'", inputPhoneNumber, expectedPhoneNumber), + expectedPhoneNumber, normalizedPhoneNumber); + } + + @Test + public void invalidNormalizedPhoneNumberTest2() { + + String expectedPhoneNumber = "359887345234"; + String inputPhoneNumber = "BG:35988734523"; + String normalizedPhoneNumber = phoneNumberValidator.getNormalizedPhoneNumber(inputPhoneNumber); + assertNotEquals( + String.format("Phone number: '%s' should be normalized to '%s'", inputPhoneNumber, expectedPhoneNumber), + expectedPhoneNumber, normalizedPhoneNumber); + } +} diff --git a/src/test/java/biz/nynja/account/components/ValidatorTests.java b/src/test/java/biz/nynja/account/components/ValidatorTests.java new file mode 100644 index 0000000000000000000000000000000000000000..e5bc6654112bbfa3643df667551d14a7d45ae4ff --- /dev/null +++ b/src/test/java/biz/nynja/account/components/ValidatorTests.java @@ -0,0 +1,249 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ + +package biz.nynja.account.components; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import biz.nynja.account.grpc.AuthenticationType; +import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; +import biz.nynja.account.grpc.CreatePendingAccountRequest; +import biz.nynja.account.grpc.ErrorResponse.Cause; +import biz.nynja.account.phone.PhoneNumberValidator; +import biz.nynja.account.repositories.AccountRepositoryAdditional; +import biz.nynja.account.utils.Util; + +/** + * Components unit tests. + */ + +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = { Validator.class, PendingAccountValidator.class, PhoneNumberValidator.class }) +public class ValidatorTests { + + @Autowired + @Qualifier("validator") + private Validator validator; + + @Autowired + @Qualifier("pendingAccountValidator") + private PendingAccountValidator pendingAccountValidator; + + @MockBean + private AccountRepositoryAdditional accountRepositoryAdditional; + + @Test + public void validUsernameTest() { + + String username = "VALID_username1"; + boolean isValid = validator.isUsernameValid(username); + assertTrue(String.format("Username: '%s' should be valid.", username), isValid == true); + + } + + @Test + public void invalidUsernameTest() { + + String username = "INVALID-username1"; + boolean isValid = validator.isUsernameValid(username); + assertFalse(String.format("Username: '%s' should be invalid.", username), isValid == true); + + } + + @Test + public void validEmailTest() { + + String email = "valid.E-mail1@domain-sub.test.com"; + boolean isValid = validator.isEmailValid(email); + assertTrue(String.format("Email: '%s' should be valid.", email), isValid == true); + + } + + @Test + public void invalidEmailTest() { + + String email = "invalid.E-mail1.@domain_test.com1"; + boolean isValid = validator.isEmailValid(email); + assertFalse(String.format("Email: '%s' should be invalid.", email), isValid == true); + + } + + @Test + public void validFistNameTest() { + + String fistName = "Validfirstname"; + boolean isValid = validator.isFirstNameValid(fistName); + assertTrue(String.format("First name: '%s' should be valid.", fistName), isValid == true); + + } + + @Test + public void invalidFirstNameTest() { + + String fistName = "VeryINVALIDFirstNameThisIsForReal"; + boolean isValid = validator.isFirstNameValid(fistName); + assertFalse(String.format("First name: '%s' should be invalid.", fistName), isValid == true); + + } + + @Test + public void validLastNameTest() { + + String lastName = "Validlastname"; + boolean isValid = validator.isLastNameValid(lastName); + assertTrue(String.format("Username: '%s' should be valid.", lastName), isValid == true); + + lastName = ""; + isValid = validator.isLastNameValid(lastName); + assertTrue(String.format("Last name: '%s' should be valid.", lastName), isValid == true); + + } + + @Test + public void invalidlastNameTest() { + + String lastName = "VeryINVALIDFirstNameThisIsForReal"; + boolean isValid = validator.isLastNameValid(lastName); + assertFalse(String.format("Last name: '%s' should be invalid.", lastName), isValid == true); + + } + + @Test + public void validAccountName() { + String accountName = "validAccountName"; + boolean isValid = validator.isAccountNameValid(accountName); + assertTrue(String.format("Account name: '%s' should be valid.", accountName), isValid); + } + + @Test + public void invalidAccountName() { + String accountName = "invalidAccountNameinvalidAccountNameinvalidAccountNameinvalidAccountNameinvalidAccountNameinvalidAccountNameinvalidAccountName"; + boolean isValid = validator.isAccountNameValid(accountName); + assertFalse(String.format("Account name: '%s' should be valid.", accountName), isValid); + } + + @Test + public void isValidUuidTestOK() { + String uuid = "11d4b22a-1470-4c18-afed-bb9283b4040f"; + assertTrue(validator.isValidUuid(uuid)); + } + + @Test + public void isValidUuidTestTooLong() { + String uuid = "11d4b22a-1470-4c18-a25b-afed-bb9283b4040f"; + assertFalse(validator.isValidUuid(uuid)); + } + + @Test + public void isValidUuidTestTooShort() { + String uuid = "11d4b22a-1470-afed-bb9283b4040f"; + assertFalse(validator.isValidUuid(uuid)); + } + + @Test + public void isValidUuidTestNotHex() { + String uuid = "11d4bp2a-1470-afed-bb9283b4040f"; + assertFalse(validator.isValidUuid(uuid)); + } + + @Test + public void isValidUuidTestHyphen() { + String uuid = "11d4bp2a--1470-afed-bb9283b4040f"; + assertFalse(validator.isValidUuid(uuid)); + } + + @Test + public void validateAuthProviderValidTest() { + assertNull("should be null", + validator.validateAuthProvider(AuthenticationType.EMAIL, "valid.E-mail1@domain-sub.test.com")); + } + + @Test + public void validateAuthProviderInvalidTest() { + assertEquals(validator.validateAuthProvider(AuthenticationType.EMAIL, "invalid.E-mail1.@domain_test.com1"), + Cause.EMAIL_INVALID); + } + + @Test + public void validateAuthProviderEmptyProviderIdentifierTest() { + assertEquals(validator.validateAuthProvider(AuthenticationType.EMAIL, null), + Cause.MISSING_AUTH_PROVIDER_IDENTIFIER); + } + + @Test + public void validateAuthProviderValidPhoneTest() { + assertNull("should be null", validator.validateAuthProvider(AuthenticationType.PHONE, "BG:+359888888888")); + } + + @Test + public void validateAuthProviderInvalidPhoneNoDotsTest() { + assertEquals(validator.validateAuthProvider(AuthenticationType.PHONE, "BG+359881111111"), + Cause.PHONE_NUMBER_INVALID); + } + + @Test + public void validateAuthProviderInvalidPhoneManyDotsTest() { + assertEquals(validator.validateAuthProvider(AuthenticationType.PHONE, "BG:+359:879555555"), + Cause.PHONE_NUMBER_INVALID); + } + + @Test + public void validateAuthProviderInvalidPhoneWrongCountryTest() { + assertEquals(validator.validateAuthProvider(AuthenticationType.PHONE, "BdasG:+359883456789"), + Cause.PHONE_NUMBER_INVALID); + } + + @Test + public void validateCompletePendingAccountCreationRequestGoodRequestTest() { + CompletePendingAccountCreationRequest request = CompletePendingAccountCreationRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setFirstName(Util.FIRST_NAME).setLastName(Util.LAST_NAME) + .setUsername(Util.USERNAME).build(); + assertNull(pendingAccountValidator.validateCompletePendingAccountCreationRequest(request)); + } + + @Test + public void validateCompletePendingAccountCreationRequestInvalidUserNameTest() { + CompletePendingAccountCreationRequest request = CompletePendingAccountCreationRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setFirstName(Util.FIRST_NAME).setLastName(Util.LAST_NAME) + .setUsername("@alabala").build(); + assertEquals(Cause.USERNAME_INVALID, + pendingAccountValidator.validateCompletePendingAccountCreationRequest(request)); + } + + @Test + public void validateCompletePendingAccountCreationRequestMissingAccountIdTest() { + CompletePendingAccountCreationRequest request = CompletePendingAccountCreationRequest.newBuilder() + .setFirstName(Util.FIRST_NAME).setLastName(Util.LAST_NAME).setUsername(Util.USERNAME).build(); + assertEquals(Cause.MISSING_ACCOUNT_ID, + pendingAccountValidator.validateCompletePendingAccountCreationRequest(request)); + } + + @Test + public void validateCreatePendingAccountRequestGoodRequestTest() { + CreatePendingAccountRequest request = CreatePendingAccountRequest.newBuilder() + .setAuthenticationType(AuthenticationType.EMAIL) + .setAuthenticationProvider("valid.E-mail1@domain-sub.test.com").build(); + assertNull("should be null", pendingAccountValidator.validateCreatePendingAccountRequest(request)); + } + + @Test + public void validateCreatePendingAccountRequestInvalidEmailTest() { + CreatePendingAccountRequest request = CreatePendingAccountRequest.newBuilder() + .setAuthenticationType(AuthenticationType.EMAIL) + .setAuthenticationProvider("invalid.E-mail1.@domain_test.com1").build(); + assertEquals(Cause.EMAIL_INVALID, pendingAccountValidator.validateCreatePendingAccountRequest(request)); + } + +} diff --git a/src/test/java/biz/nynja/account/configurations/CassandraTestsConfig.java b/src/test/java/biz/nynja/account/configurations/CassandraTestsConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..f57d3f5524930b8cec693735cb13e539ac4b6f24 --- /dev/null +++ b/src/test/java/biz/nynja/account/configurations/CassandraTestsConfig.java @@ -0,0 +1,48 @@ +package biz.nynja.account.configurations; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.data.cassandra.config.CassandraClusterFactoryBean; +import org.springframework.data.cassandra.core.CassandraOperations; +import org.springframework.data.cassandra.core.CassandraTemplate; +import org.springframework.data.cassandra.core.convert.CassandraConverter; +import org.springframework.data.cassandra.core.convert.MappingCassandraConverter; +import org.springframework.data.cassandra.core.mapping.CassandraMappingContext; +import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver; + +import com.datastax.driver.core.Session; + +@TestConfiguration +public class CassandraTestsConfig { + + @MockBean + private Session session; + + @Bean + public CassandraClusterFactoryBean cluster() { + + CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean(); + cluster.setContactPoints("127.0.0.1"); + cluster.setPort(1); + + return cluster; + } + + @Bean + public CassandraMappingContext mappingContext() { + CassandraMappingContext mappingContext = new CassandraMappingContext(); + mappingContext.setUserTypeResolver(new SimpleUserTypeResolver(cluster().getObject(), "AuthenticationProvider")); + return mappingContext; + } + + @Bean + public CassandraConverter converter() { + return new MappingCassandraConverter(mappingContext()); + } + + @Bean + public CassandraOperations cassandraTemplate() throws Exception { + return new CassandraTemplate(session, converter()); + } +} diff --git a/src/test/java/biz/nynja/account/grpc/ApplicationTests.java b/src/test/java/biz/nynja/account/grpc/ApplicationTests.java deleted file mode 100644 index c0ea2a8f27aac7219c5b209a0bf3d296b84b2287..0000000000000000000000000000000000000000 --- a/src/test/java/biz/nynja/account/grpc/ApplicationTests.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (C) 2018 Nynja Inc. All rights reserved. - */ -package biz.nynja.account.grpc; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; - -import java.util.Optional; -import java.util.concurrent.ExecutionException; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; - -import biz.nynja.account.grpc.models.AccountInfo; -import biz.nynja.account.grpc.repositories.AccountInfoRepository; -import biz.nynja.blueprint.grpc.AccountServiceGrpc; -import biz.nynja.blueprint.grpc.RegisterError; -import biz.nynja.blueprint.grpc.RegisterRequest; -import biz.nynja.blueprint.grpc.RegisterResponse; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = Util.class, - properties = { - "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration", - "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration" }) -@ActiveProfiles("dev") -public class ApplicationTests extends GrpcServerTestBase { - - @MockBean - private AccountInfoRepository accountInfoRepository; - - @Autowired - @Qualifier("newAccount") - private AccountInfo accountInfo; - - @Autowired - @Qualifier("savedAccount") - private AccountInfo savedAccount; - - @Test - public void testRegister() throws ExecutionException, InterruptedException { - - String testEmail = "email@test.com"; - final RegisterRequest request = RegisterRequest.newBuilder().setId(1).setEmail(testEmail).setPassword("abc") - .build(); - - given(accountInfoRepository.findByEmail(testEmail)).willReturn(null); - given(accountInfoRepository.save(any(AccountInfo.class))).willReturn(savedAccount); - - final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); - - final RegisterResponse reply = accountServiceBlockingStub.register(request); - - assertNotNull("Reply should not be null", reply); - assertTrue(String.format("Reply should contain email '%s'", testEmail), - reply.getAccount().getEmail().equals(testEmail)); - } - - @Test - public void testRegisterExistEmail() throws ExecutionException, InterruptedException { - - String testEmail = "email@test.com"; - final RegisterRequest request = RegisterRequest.newBuilder().setId(1).setEmail(testEmail).setPassword("abc") - .build(); - - given(accountInfoRepository.findByEmail(testEmail)).willReturn(accountInfo); - - final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); - - final RegisterResponse reply = accountServiceBlockingStub.register(request); - - assertNotNull("Reply should not be null", reply); - assertTrue(String.format("Reply should contain cause '%s'", testEmail), - reply.getError().getCause().equals(RegisterError.Cause.EMAIL_ALREADY_USED)); - } - -} diff --git a/src/test/java/biz/nynja/account/grpc/Util.java b/src/test/java/biz/nynja/account/grpc/Util.java deleted file mode 100644 index 8a9b1d945cd25b4518ecc9eadbb4e1c9e8072348..0000000000000000000000000000000000000000 --- a/src/test/java/biz/nynja/account/grpc/Util.java +++ /dev/null @@ -1,32 +0,0 @@ -package biz.nynja.account.grpc; - -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; - -import biz.nynja.account.grpc.models.AccountInfo;; - -@TestConfiguration -public class Util { - - public static final Long ID = 1L; - public static final String EMAIL = "email@test.com"; - public static final String PASSWORD = "abc"; - - @Bean - public AccountInfo newAccount() { - AccountInfo accountInfo = new AccountInfo(); - accountInfo.setId(ID); - accountInfo.setEmail(EMAIL); - accountInfo.setPassword(PASSWORD); - return accountInfo; - } - - @Bean - public AccountInfo savedAccount() { - AccountInfo accountInfo = new AccountInfo(); - accountInfo.setId(ID); - accountInfo.setEmail(EMAIL); - accountInfo.setPassword(PASSWORD); - return accountInfo; - } -} diff --git a/src/test/java/biz/nynja/account/services/AccountServiceTests.java b/src/test/java/biz/nynja/account/services/AccountServiceTests.java new file mode 100644 index 0000000000000000000000000000000000000000..8f02eedbce7a8319e013955a51a27f3f83224579 --- /dev/null +++ b/src/test/java/biz/nynja/account/services/AccountServiceTests.java @@ -0,0 +1,1718 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.services; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import biz.nynja.account.components.AccountServiceHelper; +import biz.nynja.account.components.PreparedStatementsCache; +import biz.nynja.account.configurations.CassandraTestsConfig; +import biz.nynja.account.grpc.AccountByAccountIdRequest; +import biz.nynja.account.grpc.AccountByAuthenticationProviderRequest; +import biz.nynja.account.grpc.AccountDetails; +import biz.nynja.account.grpc.AccountResponse; +import biz.nynja.account.grpc.AccountServiceGrpc; +import biz.nynja.account.grpc.AccountsByProfileIdRequest; +import biz.nynja.account.grpc.AccountsList; +import biz.nynja.account.grpc.AccountsResponse; +import biz.nynja.account.grpc.AddAuthenticationProviderRequest; +import biz.nynja.account.grpc.AddContactInfoRequest; +import biz.nynja.account.grpc.AuthProviderDetails; +import biz.nynja.account.grpc.AuthenticationType; +import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; +import biz.nynja.account.grpc.ContactDetails; +import biz.nynja.account.grpc.ContactType; +import biz.nynja.account.grpc.CreatePendingAccountRequest; +import biz.nynja.account.grpc.CreatePendingAccountResponse; +import biz.nynja.account.grpc.Date; +import biz.nynja.account.grpc.DeleteAccountRequest; +import biz.nynja.account.grpc.DeleteAuthenticationProviderRequest; +import biz.nynja.account.grpc.DeleteContactInfoRequest; +import biz.nynja.account.grpc.DeleteProfileRequest; +import biz.nynja.account.grpc.EditContactInfoRequest; +import biz.nynja.account.grpc.ErrorResponse.Cause; +import biz.nynja.account.grpc.GetByEmailRequest; +import biz.nynja.account.grpc.GetByPhoneNumberRequest; +import biz.nynja.account.grpc.GetByQrCodeRequest; +import biz.nynja.account.grpc.GetByUsernameRequest; +import biz.nynja.account.grpc.ProfileResponse; +import biz.nynja.account.grpc.Role; +import biz.nynja.account.grpc.SearchResponse; +import biz.nynja.account.grpc.StatusResponse; +import biz.nynja.account.grpc.UpdateAccountRequest; +import biz.nynja.account.grpc.UpdateProfileRequest; +import biz.nynja.account.models.Account; +import biz.nynja.account.models.AccountByAuthenticationProvider; +import biz.nynja.account.models.AccountByProfileId; +import biz.nynja.account.models.AccountByQrCode; +import biz.nynja.account.models.AccountByUsername; +import biz.nynja.account.models.AuthenticationProvider; +import biz.nynja.account.models.ContactInfo; +import biz.nynja.account.models.PendingAccount; +import biz.nynja.account.models.PendingAccountByAuthenticationProvider; +import biz.nynja.account.models.Profile; +import biz.nynja.account.models.ProfileByAuthenticationProvider; +import biz.nynja.account.repositories.AccountByAuthenticationProviderRepository; +import biz.nynja.account.repositories.AccountByProfileIdRepository; +import biz.nynja.account.repositories.AccountByQrCodeRepository; +import biz.nynja.account.repositories.AccountByUsernameRepository; +import biz.nynja.account.repositories.AccountRepository; +import biz.nynja.account.repositories.AccountRepositoryAdditional; +import biz.nynja.account.repositories.PendingAccountRepository; +import biz.nynja.account.repositories.ProfileByAuthenticationProviderRepository; +import biz.nynja.account.repositories.ProfileRepository; +import biz.nynja.account.services.decomposition.AccountProvider; +import biz.nynja.account.utils.GrpcServerTestBase; +import biz.nynja.account.utils.Util; + +/** + * AccountService unit tests. + */ + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = { Util.class, CassandraTestsConfig.class }, + webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "grpc.port=${grpc.unitTest.port.accountServiceTest}", + "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration", + "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration" }) +@ActiveProfiles("dev") +public class AccountServiceTests extends GrpcServerTestBase { + + @Autowired + @Qualifier("accountByPhone") + private AccountByAuthenticationProvider accountByPhone; + + @Autowired + @Qualifier("accountByEmail") + private AccountByAuthenticationProvider accountByEmail; + + @Autowired + @Qualifier("accountByFacebook") + private AccountByAuthenticationProvider accountByFacebook; + + @Autowired + @Qualifier("newAccount") + private Account newAccount; + + @Autowired + @Qualifier("savedPendingAccount") + private PendingAccount pendingAccount; + + @Autowired + @Qualifier("existingPendingAccount") + private PendingAccount existingPendingAccount; + + @Autowired + @Qualifier("existingPendingAccountByAuthenticationProvider") + private PendingAccountByAuthenticationProvider existingPendingAccountByAuthenticationProvider; + + @Autowired + @Qualifier("savedAccount") + private Account savedAccount; + + @Autowired + @Qualifier("updatedAccount") + private Account updatedAccount; + + @Autowired + @Qualifier("updatedProfile") + private Profile updatedProfile; + + @Autowired + @Qualifier("savedAccountByProfileId") + private AccountByProfileId savedAccountByProfileId; + + @Autowired + @Qualifier("phoneAccount") + private Account phoneAccount; + + @Autowired + @Qualifier("profile1AuthProvider") + private Profile profile1AuthProvider; + + @Autowired + @Qualifier("profile2AuthProviders") + private Profile profile2AuthProviders; + + @Autowired + @Qualifier("profileByAuthenticationProvider") + private ProfileByAuthenticationProvider profileByAuthenticationProvider; + + @Autowired + @Qualifier("savedResponse") + private AccountByUsername savedResponse; + + @Autowired + @Qualifier("savedResponseProvider") + private AccountByAuthenticationProvider savedResponseProvider; + + @Autowired + @Qualifier("savedResponseQrCode") + private AccountByQrCode savedResponseQrCode; + + @MockBean + private AccountByAuthenticationProviderRepository accountByAuthenticationProviderRepository; + + @MockBean + private PreparedStatementsCache preparedStatementsCache; + + @MockBean + private AccountRepository accountRepository; + + @MockBean + private ProfileRepository profileRepository; + + @MockBean + private PendingAccountRepository pendingAccountRepository; + + @MockBean + private AccountByProfileIdRepository accountByProffileIdRepository; + + @MockBean + private AccountRepositoryAdditional accountRepositoryAdditional; + + @MockBean + private ProfileByAuthenticationProviderRepository profileByAutheticationProviderRepository; + + @MockBean + private AccountByQrCodeRepository accountByQrCodeRepository; + + @MockBean + private AccountServiceHelper util; + + @MockBean + private AccountByUsernameRepository accountByUsernameRepository; + + @MockBean + private AccountProvider accountProvider; + + @Test + public void testGetAccountByAccountId() throws ExecutionException, InterruptedException { + final AccountByAccountIdRequest request = AccountByAccountIdRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).build(); + + Account account = savedAccount; + AccountResponse response = AccountResponse.newBuilder().setAccountDetails(account.toProto()).build(); + Optional accountResponse = Optional.of(response); + given(accountProvider.getAccountByAccountId(request)).willReturn(accountResponse); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountResponse reply = accountServiceBlockingStub.getAccountByAccountId(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain profile ID '%s'", Util.ACCOUNT_ID.toString()), + reply.getAccountDetails().getAccountId().equals(Util.ACCOUNT_ID.toString())); + } + + @Test + public void testGetAccountByAccountIdNotFound() throws ExecutionException, InterruptedException { + final AccountByAccountIdRequest request = AccountByAccountIdRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).build(); + + Account account = new Account(); + + given(accountRepository.findByAccountId(UUID.fromString(request.getAccountId()))).willReturn(account); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountResponse reply = accountServiceBlockingStub.getAccountByAccountId(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ACCOUNT_NOT_FOUND), + reply.getError().getCause().equals(Cause.ACCOUNT_NOT_FOUND)); + } + + @Test + public void testGetAccountByAccountIdBadRequest() throws ExecutionException, InterruptedException { + + final AccountByAccountIdRequest request = AccountByAccountIdRequest.newBuilder().build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountResponse reply = accountServiceBlockingStub.getAccountByAccountId(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_ACCOUNT_ID), + reply.getError().getCause().equals(Cause.MISSING_ACCOUNT_ID)); + } + + @Test + public void testGetAccountsByProfileId() throws ExecutionException, InterruptedException { + final AccountsByProfileIdRequest request = AccountsByProfileIdRequest.newBuilder() + .setProfileId(Util.PROFILE_ID.toString()).build(); + + List accountByProfileId = new ArrayList<>(); + accountByProfileId.add(savedAccountByProfileId); + + List responseList = accountByProfileId.stream().map(AccountByProfileId::toProto) + .collect(Collectors.toList()); + + AccountsResponse aResponse = AccountsResponse.newBuilder() + .setAccountsResponse(AccountsList.newBuilder().addAllAccountDetails(responseList)).build(); + Optional response = Optional.of(aResponse); + given(accountProvider.getAllAccountsByProfileId(request)).willReturn(response); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountsResponse reply = accountServiceBlockingStub.getAllAccountsByProfileId(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain profile ID '%s'", Util.PROFILE_ID.toString()), + reply.getAccountsResponse().getAccountDetails(0).getProfileId().equals(Util.PROFILE_ID.toString())); + } + + @Test + public void testGetAccountsByProfileIdNotFound() throws ExecutionException, InterruptedException { + final AccountsByProfileIdRequest request = AccountsByProfileIdRequest.newBuilder() + .setProfileId(Util.PROFILE_ID.toString()).build(); + + List accountsByProfileId = new ArrayList<>(); + + given(accountByProffileIdRepository.findAllByProfileId(Util.PROFILE_ID)).willReturn(accountsByProfileId); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountsResponse reply = accountServiceBlockingStub.getAllAccountsByProfileId(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ACCOUNT_NOT_FOUND), + reply.getError().getCause().equals(Cause.ACCOUNT_NOT_FOUND)); + } + + @Test + public void testGetAccountsByProfileIdBadRequest() throws ExecutionException, InterruptedException { + final AccountsByProfileIdRequest request = AccountsByProfileIdRequest.newBuilder().build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountsResponse reply = accountServiceBlockingStub.getAllAccountsByProfileId(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_PROFILE_ID), + reply.getError().getCause().equals(Cause.MISSING_PROFILE_ID)); + } + + @Test + public void testGetAccountByAuthProviderPhone() { + final AccountByAuthenticationProviderRequest request = AccountByAuthenticationProviderRequest.newBuilder() + .setAuthenticationIdentifier(Util.PHONE_PROVIDER).setAuthenticationType(AuthenticationType.PHONE) + .build(); + + AccountResponse response = AccountResponse.newBuilder().setAccountDetails(accountByPhone.toProto()).build(); + Optional accountResponse = Optional.of(response); + + given(accountProvider.getAccountResponseByAuthenticationProvider(AuthenticationType.PHONE, + Util.PHONE_PROVIDER)).willReturn(accountResponse); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse reply = accountServiceBlockingStub.getAccountByAuthenticationProvider(request); + assertNotNull("Reply should not be null", reply); + assertTrue( + String.format("Reply should contain authentication provider '%s'", + request.getAuthenticationIdentifier()), + reply.getAccountDetails().getAuthenticationIdentifier().equals(Util.PHONE_NUMBER)); + assertTrue(String.format("Reply should contain authentication type '%s'", AuthenticationType.PHONE.name()), + reply.getAccountDetails().getAuthenticationType().equals(AuthenticationType.PHONE.name())); + } + + @Test + public void testGetAccountByAuthProviderMissingType() { + final AccountByAuthenticationProviderRequest request = AccountByAuthenticationProviderRequest.newBuilder() + .build(); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse reply = accountServiceBlockingStub.getAccountByAuthenticationProvider(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_AUTH_PROVIDER_TYPE), + reply.getError().getCause().equals(Cause.MISSING_AUTH_PROVIDER_TYPE)); + } + + @Test + public void testGetAccountByAuthProviderMissingIdentifier() { + final AccountByAuthenticationProviderRequest request = AccountByAuthenticationProviderRequest.newBuilder() + .setAuthenticationType(AuthenticationType.PHONE).build(); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse reply = accountServiceBlockingStub.getAccountByAuthenticationProvider(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_AUTH_PROVIDER_IDENTIFIER), + reply.getError().getCause().equals(Cause.MISSING_AUTH_PROVIDER_IDENTIFIER)); + } + + @Test + public void testGetAccountByAuthProviderNotFound() { + final AccountByAuthenticationProviderRequest request = AccountByAuthenticationProviderRequest.newBuilder() + .setAuthenticationIdentifier(Util.EMAIL).setAuthenticationType(AuthenticationType.EMAIL).build(); + List accList = new ArrayList<>(); + given(accountByAuthenticationProviderRepository.findAllByAuthenticationProvider(Util.EMAIL)) + .willReturn(accList); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse reply = accountServiceBlockingStub.getAccountByAuthenticationProvider(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ACCOUNT_NOT_FOUND), + reply.getError().getCause().equals(Cause.ACCOUNT_NOT_FOUND)); + } + + @Test + public void testUpdateAccount() throws ExecutionException, InterruptedException { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder().setAccountId(Util.ACCOUNT_ID.toString()) + .setAccountMark(Util.UPDATED_ACCOUNT_MARK).setFirstName(Util.FIRST_NAME).build(); + given(accountRepositoryAdditional.updateAccount(request)).willReturn(updatedAccount); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain account mark '%s'", Util.UPDATED_ACCOUNT_MARK), + reply.getAccountDetails().getAccountMark().equals(Util.UPDATED_ACCOUNT_MARK)); + } + + @Test + public void testUpdateAccountMissingFirstName() { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder().setAccountId(Util.ACCOUNT_ID.toString()) + .setAccountMark(Util.UPDATED_ACCOUNT_MARK).build(); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_FIRST_NAME), + reply.getError().getCause().equals(Cause.MISSING_FIRST_NAME)); + } + + @Test + public void testUpdateAccountMissingAccountId() { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder().setFirstName(Util.FIRST_NAME) + .setAccountMark(Util.UPDATED_ACCOUNT_MARK).build(); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_ACCOUNT_ID), + reply.getError().getCause().equals(Cause.MISSING_ACCOUNT_ID)); + } + + @Test + public void testUpdateAccountAccountIdNotFound() { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID_NOT_FOUND.toString()).setFirstName(Util.FIRST_NAME) + .setAccountMark(Util.UPDATED_ACCOUNT_MARK).build(); + given(accountRepositoryAdditional.updateAccount(request)).willReturn(null); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_UPDATING_ACCOUNT), + reply.getError().getCause().equals(Cause.ERROR_UPDATING_ACCOUNT)); + } + + @Test + public void testUpdateAccountUsernameAlreadyUsed() { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder().setAccountId(Util.ACCOUNT_ID.toString()) + .setAccountMark(Util.UPDATED_ACCOUNT_MARK).setFirstName(Util.FIRST_NAME).setUsername(Util.USERNAME) + .build(); + given(accountRepositoryAdditional.foundExistingNotOwnUsername(UUID.fromString(request.getAccountId()), + request.getUsername())).willReturn(true); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.USERNAME_ALREADY_USED), + reply.getError().getCause().equals(Cause.USERNAME_ALREADY_USED)); + } + + @Test + public void testCreatePendingAccountAuthProviderAlreadyUsedInPendingAccount() { + final CreatePendingAccountRequest request = CreatePendingAccountRequest.newBuilder() + .setAuthenticationType(AuthenticationType.EMAIL).setAuthenticationProvider(Util.EMAIL).build(); + given(accountRepositoryAdditional.findSameAuthenticationProviderInPendingAccount( + AuthenticationProvider.createAuthenticationProviderFromStrings(request.getAuthenticationType().name(), + request.getAuthenticationProvider()))) + .willReturn(existingPendingAccountByAuthenticationProvider); + given(pendingAccountRepository.save(Mockito.any(PendingAccount.class))).willReturn(existingPendingAccount); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final CreatePendingAccountResponse reply = accountServiceBlockingStub.createPendingAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain account id '%s'", Util.ACCOUNT_ID), + reply.getPendingAccountDetails().getAccountId().equals(Util.ACCOUNT_ID.toString())); + } + + @Test + public void testCreatePendingAccountAuthProviderAlreadyUsedInAccount() { + final CreatePendingAccountRequest request = CreatePendingAccountRequest.newBuilder() + .setAuthenticationType(AuthenticationType.EMAIL).setAuthenticationProvider(Util.EMAIL).build(); + given(accountRepositoryAdditional.authenticationProviderAlreadyUsedInAccount( + AuthenticationProvider.createAuthenticationProviderFromStrings(request.getAuthenticationType().name(), + request.getAuthenticationProvider()))).willReturn(true); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final CreatePendingAccountResponse reply = accountServiceBlockingStub.createPendingAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain account id '%s'", Cause.ACCOUNT_ALREADY_CREATED), + reply.getError().getCause().equals(Cause.ACCOUNT_ALREADY_CREATED)); + } + + @Test + public void testUpdateProfileMissingProfileId() throws ExecutionException, InterruptedException { + final UpdateProfileRequest request = UpdateProfileRequest.newBuilder().build(); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final ProfileResponse reply = accountServiceBlockingStub.updateProfile(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_PROFILE_ID), + reply.getError().getCause().equals(Cause.MISSING_PROFILE_ID)); + } + + @Test + public void testCreatePendingAccountOK() { + final CreatePendingAccountRequest request = CreatePendingAccountRequest.newBuilder() + .setAuthenticationType(AuthenticationType.EMAIL).setAuthenticationProvider(Util.EMAIL).build(); + given(pendingAccountRepository.save(Mockito.any(PendingAccount.class))).willReturn(pendingAccount); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final CreatePendingAccountResponse reply = accountServiceBlockingStub.createPendingAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain account id '%s'", Util.ACCOUNT_ID), + reply.getPendingAccountDetails().getAccountId().equals(Util.ACCOUNT_ID.toString())); + } + + @Test + public void testCreatePendingAccountInvalidEmail() { + final CreatePendingAccountRequest request = CreatePendingAccountRequest.newBuilder() + .setAuthenticationType(AuthenticationType.EMAIL) + .setAuthenticationProvider("invalid.E-mail1.@domain_test.com1").build(); + given(pendingAccountRepository.save(Mockito.any(PendingAccount.class))).willReturn(pendingAccount); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final CreatePendingAccountResponse reply = accountServiceBlockingStub.createPendingAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.EMAIL_INVALID), + reply.getError().getCause().equals(Cause.EMAIL_INVALID)); + } + + @Test + public void testCreatePendingAccountInvalidPhone() { + final CreatePendingAccountRequest request = CreatePendingAccountRequest.newBuilder() + .setAuthenticationType(AuthenticationType.PHONE).setAuthenticationProvider("BG:084365:5555").build(); + given(pendingAccountRepository.save(Mockito.any(PendingAccount.class))).willReturn(pendingAccount); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final CreatePendingAccountResponse reply = accountServiceBlockingStub.createPendingAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.PHONE_NUMBER_INVALID), + reply.getError().getCause().equals(Cause.PHONE_NUMBER_INVALID)); + } + + @Test + public void testCompletePendingAccountCreationOK() { + final CompletePendingAccountCreationRequest request = CompletePendingAccountCreationRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setUsername(Util.USERNAME).setFirstName(Util.FIRST_NAME) + .build(); + given(accountRepositoryAdditional + .completePendingAccountCreation(Mockito.any(CompletePendingAccountCreationRequest.class))) + .willReturn(savedAccount); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse respose = accountServiceBlockingStub.completePendingAccountCreation(request); + assertNotNull("Reply should not be null", respose); + assertTrue(String.format("Reply should contain username '%s'", Util.USERNAME), + respose.getAccountDetails().getUsername().equals(Util.USERNAME)); + assertTrue(String.format("Reply should contain account id '%s'", Util.ACCOUNT_ID), + respose.getAccountDetails().getAccountId().equals(Util.ACCOUNT_ID.toString())); + } + + @Test + public void testCompletePendingAccountCreationOkMissingRole() { + final CompletePendingAccountCreationRequest request = CompletePendingAccountCreationRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setUsername(Util.USERNAME).setFirstName(Util.FIRST_NAME) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + accountServiceBlockingStub.completePendingAccountCreation(request); + + ArgumentCaptor argument = ArgumentCaptor + .forClass(CompletePendingAccountCreationRequest.class); + verify(accountRepositoryAdditional).completePendingAccountCreation(argument.capture()); + assertNotNull(argument.getValue().getRolesList()); + assertNotNull(Role.USER.equals(argument.getValue().getRoles(0))); + } + + @Test + public void testCompletePendingAccountCreationOkMultipleRoles() { + final CompletePendingAccountCreationRequest request = CompletePendingAccountCreationRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setUsername(Util.USERNAME).setFirstName(Util.FIRST_NAME) + .addRoles(Role.USER).addRoles(Role.ADMIN).build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + accountServiceBlockingStub.completePendingAccountCreation(request); + + ArgumentCaptor argument = ArgumentCaptor + .forClass(CompletePendingAccountCreationRequest.class); + verify(accountRepositoryAdditional).completePendingAccountCreation(argument.capture()); + assertNotNull(argument.getValue().getRolesList()); + assertNotNull(Role.USER.equals(argument.getValue().getRoles(0))); + assertNotNull(Role.USER.equals(argument.getValue().getRoles(1))); + } + + @Test + public void testCompletePendingAccountCreationUsernameInvalid() { + final CompletePendingAccountCreationRequest request = CompletePendingAccountCreationRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setUsername("Invalid!Username").setFirstName(Util.FIRST_NAME) + .build(); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse respose = accountServiceBlockingStub.completePendingAccountCreation(request); + assertNotNull("Reply should not be null", respose); + assertTrue(String.format("Reply should contain cause '%s'", Cause.USERNAME_INVALID), + respose.getError().getCause().equals(Cause.USERNAME_INVALID)); + } + + @Test + public void testCompletePendingAccountCreationMissingFirstName() { + final CompletePendingAccountCreationRequest request = CompletePendingAccountCreationRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setUsername(Util.USERNAME).build(); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse respose = accountServiceBlockingStub.completePendingAccountCreation(request); + assertNotNull("Reply should not be null", respose); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_FIRST_NAME), + respose.getError().getCause().equals(Cause.MISSING_FIRST_NAME)); + } + + @Test + public void testCompletePendingAccountCreationUsernameAlreadyUsed() { + final CompletePendingAccountCreationRequest request = CompletePendingAccountCreationRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setAccountMark(Util.UPDATED_ACCOUNT_MARK) + .setFirstName(Util.FIRST_NAME).setUsername(Util.USERNAME).build(); + given(accountRepositoryAdditional.foundExistingNotOwnUsername(UUID.fromString(request.getAccountId()), + request.getUsername())).willReturn(true); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse reply = accountServiceBlockingStub.completePendingAccountCreation(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.USERNAME_ALREADY_USED), + reply.getError().getCause().equals(Cause.USERNAME_ALREADY_USED)); + } + + @Test + public void testCompletePendingAccountCreationInvalidLastName() { + final CompletePendingAccountCreationRequest request = CompletePendingAccountCreationRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setUsername(Util.USERNAME).setFirstName(Util.FIRST_NAME) + .setLastName("VeryInvalidLastNameRepeatVeryInvalidLastNameRepeatVeryInvalidLastNameRepeat").build(); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse respose = accountServiceBlockingStub.completePendingAccountCreation(request); + assertNotNull("Reply should not be null", respose); + assertTrue(String.format("Reply should contain cause '%s'", Cause.INVALID_LAST_NAME), + respose.getError().getCause().equals(Cause.INVALID_LAST_NAME)); + } + + @Test + public void testCompletePendingAccountCreationCreatedAccountIsNull() { + final CompletePendingAccountCreationRequest request = CompletePendingAccountCreationRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setUsername(Util.USERNAME).setFirstName(Util.FIRST_NAME) + .build(); + + given(accountRepositoryAdditional + .completePendingAccountCreation(Mockito.any(CompletePendingAccountCreationRequest.class))) + .willReturn(null); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountResponse respose = accountServiceBlockingStub.completePendingAccountCreation(request); + assertNotNull("Reply should not be null", respose); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_CREATING_ACCOUNT), + respose.getError().getCause().equals(Cause.ERROR_CREATING_ACCOUNT)); + + } + + @Test + public void testDeleteAccountOK() throws ExecutionException, InterruptedException { + final DeleteAccountRequest request = DeleteAccountRequest.newBuilder().setAccountId(Util.ACCOUNT_ID.toString()) + .build(); + given(accountRepositoryAdditional.deleteAccount(Util.ACCOUNT_ID)).willReturn(true); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final StatusResponse reply = accountServiceBlockingStub.deleteAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain status '%s'", "SUCCESS"), reply.getStatus().equals("SUCCESS")); + } + + @Test + public void testDeleteAccountMissingAccountId() throws ExecutionException, InterruptedException { + final DeleteAccountRequest request = DeleteAccountRequest.newBuilder().build(); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final StatusResponse reply = accountServiceBlockingStub.deleteAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_ACCOUNT_ID), + reply.getError().getCause().equals(Cause.MISSING_ACCOUNT_ID)); + } + + @Test + public void testDeleteAccountAccountIdNotFound() throws ExecutionException, InterruptedException { + final DeleteAccountRequest request = DeleteAccountRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID_NOT_FOUND.toString()).build(); + given(accountRepository.findByAccountId(Util.ACCOUNT_ID_NOT_FOUND)).willReturn(null); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final StatusResponse reply = accountServiceBlockingStub.deleteAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_DELETING_ACCOUNT), + reply.getError().getCause().equals(Cause.ERROR_DELETING_ACCOUNT)); + } + + @Test + public void testDeleteAccountNoCorrespondingProfileFound() throws ExecutionException, InterruptedException { + final DeleteAccountRequest request = DeleteAccountRequest.newBuilder().setAccountId(Util.ACCOUNT_ID.toString()) + .build(); + given(profileRepository.findByProfileId(Util.PROFILE_ID)).willReturn(null); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final StatusResponse reply = accountServiceBlockingStub.deleteAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_DELETING_ACCOUNT), + reply.getError().getCause().equals(Cause.ERROR_DELETING_ACCOUNT)); + } + + @Test + public void testDeleteProfileOK() throws ExecutionException, InterruptedException { + final DeleteProfileRequest request = DeleteProfileRequest.newBuilder().setProfileId(Util.PROFILE_ID.toString()) + .build(); + given(accountRepositoryAdditional.deleteProfile(Util.PROFILE_ID)).willReturn(true); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final StatusResponse reply = accountServiceBlockingStub.deleteProfile(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain status '%s'", "SUCCESS"), reply.getStatus().equals("SUCCESS")); + } + + @Test + public void testDeleteProfileMissingProfileId() throws ExecutionException, InterruptedException { + final DeleteProfileRequest request = DeleteProfileRequest.newBuilder().build(); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final StatusResponse reply = accountServiceBlockingStub.deleteProfile(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_PROFILE_ID), + reply.getError().getCause().equals(Cause.MISSING_PROFILE_ID)); + } + + @Test + public void testDeleteProfileProfileIdNotFound() throws ExecutionException, InterruptedException { + final DeleteProfileRequest request = DeleteProfileRequest.newBuilder() + .setProfileId(Util.PROFILE_ID_NOT_FOUND.toString()).build(); + given(profileRepository.findByProfileId(Util.PROFILE_ID_NOT_FOUND)).willReturn(null); + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final StatusResponse reply = accountServiceBlockingStub.deleteProfile(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_DELETING_PROFILE), + reply.getError().getCause().equals(Cause.ERROR_DELETING_PROFILE)); + } + + @Test + public void testAddAuthenticationProviderToProfileOK() { + final AddAuthenticationProviderRequest request = AddAuthenticationProviderRequest.newBuilder() + .setProfileId(Util.PROFILE_ID.toString()) + .setAuthenticationProvider(AuthProviderDetails.newBuilder() + .setAuthenticationProvider(Util.PHONE_PROVIDER).setAuthenticationType(AuthenticationType.PHONE)) + .build(); + + given(accountRepositoryAdditional.addAuthenticationProvider(Mockito.any(UUID.class), + Mockito.any(AuthenticationProvider.class))).willReturn(true); + + given(profileRepository.findByProfileId(Util.PROFILE_ID)).willReturn(profile2AuthProviders); + + given(profileByAutheticationProviderRepository + .findByAuthenticationProviderAndAuthenticationProviderType(Util.PHONE_PROVIDER, "PHONE")) + .willReturn(null); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addAuthenticationProviderToProfile(request); + assertNotNull("Reply should not be null", reply); + assertEquals("SUCCESS", reply.getStatus()); + } + + @Test + public void testAddAuthenticationProviderToProfileAuthProviderUsed() { + final AddAuthenticationProviderRequest request = AddAuthenticationProviderRequest.newBuilder() + .setProfileId(Util.PROFILE_ID.toString()) + .setAuthenticationProvider(AuthProviderDetails.newBuilder() + .setAuthenticationProvider(Util.PHONE_PROVIDER).setAuthenticationType(AuthenticationType.PHONE)) + .build(); + + given(accountRepositoryAdditional.addAuthenticationProvider(Mockito.any(UUID.class), + Mockito.any(AuthenticationProvider.class))).willReturn(true); + + given(profileRepository.findByProfileId(Util.PROFILE_ID)).willReturn(profile2AuthProviders); + + given(profileByAutheticationProviderRepository.findByAuthenticationProviderAndAuthenticationProviderType( + Util.PHONE_NUMBER_STREIGHT, AuthenticationType.PHONE.name())) + .willReturn(profileByAuthenticationProvider); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addAuthenticationProviderToProfile(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.AUTH_PROVIDER_ALREADY_USED), + reply.getError().getCause().equals(Cause.AUTH_PROVIDER_ALREADY_USED)); + } + + @Test + public void testAddAuthenticationProviderToProfileAuthProviderIdMissing() { + final AddAuthenticationProviderRequest request = AddAuthenticationProviderRequest.newBuilder() + .setAuthenticationProvider(AuthProviderDetails.newBuilder() + .setAuthenticationProvider(Util.PHONE_PROVIDER).setAuthenticationType(AuthenticationType.PHONE)) + .build(); + + given(accountRepositoryAdditional.addAuthenticationProvider(Mockito.any(UUID.class), + Mockito.any(AuthenticationProvider.class))).willReturn(true); + + given(profileRepository.findByProfileId(Util.PROFILE_ID)).willReturn(profile2AuthProviders); + + given(profileByAutheticationProviderRepository + .findByAuthenticationProviderAndAuthenticationProviderType(Util.PHONE_PROVIDER, "PHONE")) + .willReturn(null); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addAuthenticationProviderToProfile(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_PROFILE_ID), + reply.getError().getCause().equals(Cause.MISSING_PROFILE_ID)); + } + + @Test + public void testAddAuthenticationProviderToProfileMissingAuthProvider() { + final AddAuthenticationProviderRequest request = AddAuthenticationProviderRequest.newBuilder() + .setProfileId(Util.PROFILE_ID.toString()).setAuthenticationProvider( + AuthProviderDetails.newBuilder().setAuthenticationType(AuthenticationType.PHONE)) + .build(); + + given(accountRepositoryAdditional.addAuthenticationProvider(Mockito.any(UUID.class), + Mockito.any(AuthenticationProvider.class))).willReturn(true); + + given(profileRepository.findByProfileId(Util.PROFILE_ID)).willReturn(profile2AuthProviders); + + given(profileByAutheticationProviderRepository + .findByAuthenticationProviderAndAuthenticationProviderType(Util.PHONE_PROVIDER, "PHONE")) + .willReturn(null); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addAuthenticationProviderToProfile(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_AUTH_PROVIDER_IDENTIFIER), + reply.getError().getCause().equals(Cause.MISSING_AUTH_PROVIDER_IDENTIFIER)); + } + + @Test + public void testAddAuthenticationProviderToProfileInternalServerError() { + final AddAuthenticationProviderRequest request = AddAuthenticationProviderRequest.newBuilder() + .setProfileId(Util.PROFILE_ID.toString()) + .setAuthenticationProvider(AuthProviderDetails.newBuilder() + .setAuthenticationProvider(Util.PHONE_PROVIDER).setAuthenticationType(AuthenticationType.PHONE)) + .build(); + + given(accountRepositoryAdditional.addAuthenticationProvider(Mockito.any(UUID.class), + Mockito.any(AuthenticationProvider.class))).willReturn(false); + + given(profileRepository.findByProfileId(Util.PROFILE_ID)).willReturn(profile2AuthProviders); + + given(profileByAutheticationProviderRepository + .findByAuthenticationProviderAndAuthenticationProviderType(Util.PHONE_PROVIDER, "PHONE")) + .willReturn(null); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addAuthenticationProviderToProfile(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.INTERNAL_SERVER_ERROR), + reply.getError().getCause().equals(Cause.INTERNAL_SERVER_ERROR)); + } + + @Test + public void testAddAuthenticationProviderToProfileNotFound() { + final AddAuthenticationProviderRequest request = AddAuthenticationProviderRequest.newBuilder() + .setProfileId(Util.PROFILE_ID.toString()) + .setAuthenticationProvider(AuthProviderDetails.newBuilder() + .setAuthenticationProvider(Util.PHONE_PROVIDER).setAuthenticationType(AuthenticationType.PHONE)) + .build(); + + given(accountRepositoryAdditional.addAuthenticationProvider(Mockito.any(UUID.class), + Mockito.any(AuthenticationProvider.class))).willReturn(true); + + given(profileRepository.findByProfileId(Util.PROFILE_ID)).willReturn(null); + + given(profileByAutheticationProviderRepository + .findByAuthenticationProviderAndAuthenticationProviderType(Util.PHONE_PROVIDER, "PHONE")) + .willReturn(profileByAuthenticationProvider); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addAuthenticationProviderToProfile(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.PROFILE_NOT_FOUND), + reply.getError().getCause().equals(Cause.PROFILE_NOT_FOUND)); + } + + @Test + public void testDeleteAuthenticationProviderFromProfileOK() { + final DeleteAuthenticationProviderRequest request = DeleteAuthenticationProviderRequest.newBuilder() + .setProfileId(Util.PROFILE_ID.toString()) + .setAuthenticationProvider(AuthProviderDetails.newBuilder() + .setAuthenticationProvider(Util.PHONE_PROVIDER).setAuthenticationType(AuthenticationType.PHONE)) + .build(); + + given(accountRepositoryAdditional.deleteAuthenticationProvider(Mockito.any(Profile.class), + Mockito.any(AuthenticationProvider.class))).willReturn(true); + + given(profileRepository.findByProfileId(Util.PROFILE_ID)).willReturn(profile2AuthProviders); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteAuthenticationProviderFromProfile(request); + assertNotNull("Reply should not be null", reply); + assertEquals("SUCCESS", reply.getStatus()); + + } + + @Test + public void testDeleteAuthenticationProviderFromProfileMissingProfileId() { + final DeleteAuthenticationProviderRequest request = DeleteAuthenticationProviderRequest.newBuilder() + .setAuthenticationProvider(AuthProviderDetails.newBuilder() + .setAuthenticationProvider(Util.PHONE_PROVIDER).setAuthenticationType(AuthenticationType.PHONE)) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteAuthenticationProviderFromProfile(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_PROFILE_ID), + reply.getError().getCause().equals(Cause.MISSING_PROFILE_ID)); + } + + @Test + public void testDeleteAuthenticationProviderFromProfileProfileNotFound() { + final DeleteAuthenticationProviderRequest request = DeleteAuthenticationProviderRequest.newBuilder() + .setProfileId(Util.PROFILE_ID.toString()) + .setAuthenticationProvider(AuthProviderDetails.newBuilder() + .setAuthenticationProvider(Util.PHONE_PROVIDER).setAuthenticationType(AuthenticationType.PHONE)) + .build(); + given(profileRepository.findByProfileId(Util.PROFILE_ID)).willReturn(null); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteAuthenticationProviderFromProfile(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.PROFILE_NOT_FOUND), + reply.getError().getCause().equals(Cause.PROFILE_NOT_FOUND)); + } + + @Test + public void testDeleteAuthenticationProviderFromProfileMinNumberOfAuthProviders() { + final DeleteAuthenticationProviderRequest request = DeleteAuthenticationProviderRequest.newBuilder() + .setProfileId(Util.PROFILE_ID.toString()) + .setAuthenticationProvider(AuthProviderDetails.newBuilder() + .setAuthenticationProvider(Util.PHONE_PROVIDER).setAuthenticationType(AuthenticationType.PHONE)) + .build(); + + given(profileRepository.findByProfileId(Util.PROFILE_ID)).willReturn(profile1AuthProvider); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteAuthenticationProviderFromProfile(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_DELETING_AUTH_PROVIDER), + reply.getError().getCause().equals(Cause.ERROR_DELETING_AUTH_PROVIDER)); + } + + @Test + public void testAddContactInfoPhoneToAccountOK() { + final AddContactInfoRequest request = AddContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setContactInfo(ContactDetails.newBuilder() + .setType(ContactType.PHONE_CONTACT).setValue(Util.PHONE_PROVIDER).setLabel("testLabel").build()) + .build(); + given(accountRepositoryAdditional.addContactInfo(Mockito.any(UUID.class), Mockito.any(ContactInfo.class))) + .willReturn(true); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addContactInfoToAccount(request); + assertNotNull("Reply should not be null", reply); + assertEquals("SUCCESS", reply.getStatus()); + } + + @Test + public void testAddContactInfoEmailToAccountOK() { + final AddContactInfoRequest request = AddContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .build(); + given(accountRepositoryAdditional.addContactInfo(Mockito.any(UUID.class), Mockito.any(ContactInfo.class))) + .willReturn(true); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addContactInfoToAccount(request); + assertNotNull("Reply should not be null", reply); + assertEquals("SUCCESS", reply.getStatus()); + } + + @Test + public void testAddContactInfoToAccountInvalidAccountId() { + final AddContactInfoRequest request = AddContactInfoRequest.newBuilder().setAccountId(Util.INVALID_ACCOUNT_ID) + .setContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addContactInfoToAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.INVALID_ACCOUNT_ID), + reply.getError().getCause().equals(Cause.INVALID_ACCOUNT_ID)); + } + + @Test + public void testAddContactInfoToAccountMissingAccountId() { + final AddContactInfoRequest request = AddContactInfoRequest.newBuilder() + .setContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addContactInfoToAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_ACCOUNT_ID), + reply.getError().getCause().equals(Cause.MISSING_ACCOUNT_ID)); + } + + @Test + public void testAddContactInfoToAccountMissingContactInfoType() { + final AddContactInfoRequest request = AddContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setContactInfo(ContactDetails.newBuilder().setValue(Util.EMAIL).build()).build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addContactInfoToAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_CONTACT_INFO_TYPE), + reply.getError().getCause().equals(Cause.MISSING_CONTACT_INFO_TYPE)); + } + + @Test + public void testAddContactInfoToAccountMissingContactInfoIdentifier() { + final AddContactInfoRequest request = AddContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setContactInfo(ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).build()).build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addContactInfoToAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_CONTACT_INFO_IDENTIFIER), + reply.getError().getCause().equals(Cause.MISSING_CONTACT_INFO_IDENTIFIER)); + } + + @Test + public void testAddContactInfoToAccountInvalidPhone() { + final AddContactInfoRequest request = AddContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setContactInfo(ContactDetails.newBuilder().setType(ContactType.PHONE_CONTACT) + .setValue(Util.INVALID_PHONE_PROVIDER).setLabel("testLabel").build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addContactInfoToAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.PHONE_NUMBER_INVALID), + reply.getError().getCause().equals(Cause.PHONE_NUMBER_INVALID)); + } + + @Test + public void testAddContactInfoToAccountInvalidEmail() { + final AddContactInfoRequest request = AddContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setContactInfo(ContactDetails.newBuilder() + .setType(ContactType.EMAIL_CONTACT).setValue(Util.INVALID_EMAIL).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addContactInfoToAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.EMAIL_INVALID), + reply.getError().getCause().equals(Cause.EMAIL_INVALID)); + } + + @Test + public void testAddContactInfoEmailToAccountErrorAddingContactInfo() { + final AddContactInfoRequest request = AddContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .build(); + given(accountRepositoryAdditional.addContactInfo(Mockito.any(UUID.class), Mockito.any(ContactInfo.class))) + .willReturn(false); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.addContactInfoToAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_ADDING_CONTACT_INFO), + reply.getError().getCause().equals(Cause.ERROR_ADDING_CONTACT_INFO)); + } + + @Test + public void testRemoveContactInfoPhoneFromAccountOK() { + final DeleteContactInfoRequest request = DeleteContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setContactInfo(ContactDetails.newBuilder() + .setType(ContactType.PHONE_CONTACT).setValue(Util.PHONE_PROVIDER).setLabel("testLabel").build()) + .build(); + given(accountRepositoryAdditional.deleteContactInfo(Mockito.any(UUID.class), Mockito.any(ContactInfo.class))) + .willReturn(true); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteContactInfoFromAccount(request); + assertNotNull("Reply should not be null", reply); + assertEquals("SUCCESS", reply.getStatus()); + } + + @Test + public void testRemoveContactInfoEmailFromAccountOK() { + final DeleteContactInfoRequest request = DeleteContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .build(); + given(accountRepositoryAdditional.deleteContactInfo(Mockito.any(UUID.class), Mockito.any(ContactInfo.class))) + .willReturn(true); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteContactInfoFromAccount(request); + assertNotNull("Reply should not be null", reply); + assertEquals("SUCCESS", reply.getStatus()); + } + + @Test + public void testRemoveContactInfoFromAccountInvalidAccountId() { + final DeleteContactInfoRequest request = DeleteContactInfoRequest.newBuilder() + .setAccountId(Util.INVALID_ACCOUNT_ID) + .setContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteContactInfoFromAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.INVALID_ACCOUNT_ID), + reply.getError().getCause().equals(Cause.INVALID_ACCOUNT_ID)); + } + + @Test + public void testRemoveContactInfoFromAccountMissingAccountId() { + final DeleteContactInfoRequest request = DeleteContactInfoRequest.newBuilder() + .setContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteContactInfoFromAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_ACCOUNT_ID), + reply.getError().getCause().equals(Cause.MISSING_ACCOUNT_ID)); + } + + @Test + public void testRemoveContactInfoFromAccountMissingContactInfoType() { + final DeleteContactInfoRequest request = DeleteContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setContactInfo(ContactDetails.newBuilder().setValue(Util.EMAIL).build()).build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteContactInfoFromAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_CONTACT_INFO_TYPE), + reply.getError().getCause().equals(Cause.MISSING_CONTACT_INFO_TYPE)); + } + + @Test + public void testRemoveContactInfoFromAccountMissingContactInfoIdentifier() { + final DeleteContactInfoRequest request = DeleteContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setContactInfo(ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).build()).build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteContactInfoFromAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_CONTACT_INFO_IDENTIFIER), + reply.getError().getCause().equals(Cause.MISSING_CONTACT_INFO_IDENTIFIER)); + } + + @Test + public void testRemoveContactInfoFromAccountInvalidPhone() { + final DeleteContactInfoRequest request = DeleteContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setContactInfo(ContactDetails.newBuilder().setType(ContactType.PHONE_CONTACT) + .setValue(Util.INVALID_PHONE_PROVIDER).setLabel("testLabel").build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteContactInfoFromAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.PHONE_NUMBER_INVALID), + reply.getError().getCause().equals(Cause.PHONE_NUMBER_INVALID)); + } + + @Test + public void testRemoveContactInfoFromAccountInvalidEmail() { + final DeleteContactInfoRequest request = DeleteContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()).setContactInfo(ContactDetails.newBuilder() + .setType(ContactType.EMAIL_CONTACT).setValue(Util.INVALID_EMAIL).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteContactInfoFromAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.EMAIL_INVALID), + reply.getError().getCause().equals(Cause.EMAIL_INVALID)); + } + + @Test + public void testRemoveContactInfoEmailFromAccountErrorRemovingContactInfo() { + final DeleteContactInfoRequest request = DeleteContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .build(); + given(accountRepositoryAdditional.addContactInfo(Mockito.any(UUID.class), Mockito.any(ContactInfo.class))) + .willReturn(false); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.deleteContactInfoFromAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_REMOVING_CONTACT_INFO), + reply.getError().getCause().equals(Cause.ERROR_REMOVING_CONTACT_INFO)); + } + + @Test + public void testEditContactInfoPhoneForAccountOK() { + final EditContactInfoRequest request = EditContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setOldContactInfo(ContactDetails.newBuilder().setType(ContactType.PHONE_CONTACT) + .setValue(Util.PHONE_PROVIDER).setLabel("testLabel").build()) + .setEditedContactInfo(ContactDetails.newBuilder().setType(ContactType.PHONE_CONTACT) + .setValue(Util.PHONE_PROVIDER_EDITED).setLabel("testLabel").build()) + .build(); + given(accountRepositoryAdditional.editContactInfo(Mockito.any(UUID.class), Mockito.any(ContactInfo.class), + Mockito.any(ContactInfo.class))).willReturn(true); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.editContactInfoForAccount(request); + assertNotNull("Reply should not be null", reply); + assertEquals("SUCCESS", reply.getStatus()); + } + + @Test + public void testEditContactInfoEmailForAccountOK() { + final EditContactInfoRequest request = EditContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setOldContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .setEditedContactInfo(ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT) + .setValue(Util.EMAIL_EDITED).build()) + .build(); + given(accountRepositoryAdditional.editContactInfo(Mockito.any(UUID.class), Mockito.any(ContactInfo.class), + Mockito.any(ContactInfo.class))).willReturn(true); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.editContactInfoForAccount(request); + assertNotNull("Reply should not be null", reply); + assertEquals("SUCCESS", reply.getStatus()); + } + + @Test + public void testEditContactInfoForAccountInvalidAccountId() { + final EditContactInfoRequest request = EditContactInfoRequest.newBuilder().setAccountId(Util.INVALID_ACCOUNT_ID) + .setOldContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .setEditedContactInfo(ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT) + .setValue(Util.EMAIL_EDITED).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.editContactInfoForAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.INVALID_ACCOUNT_ID), + reply.getError().getCause().equals(Cause.INVALID_ACCOUNT_ID)); + } + + @Test + public void testEditContactInfoForAccountMissingAccountId() { + final EditContactInfoRequest request = EditContactInfoRequest.newBuilder() + .setOldContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .setEditedContactInfo(ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT) + .setValue(Util.EMAIL_EDITED).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.editContactInfoForAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_ACCOUNT_ID), + reply.getError().getCause().equals(Cause.MISSING_ACCOUNT_ID)); + } + + @Test + public void testEditContactInfoForAccountMissingContactInfoType() { + final EditContactInfoRequest request = EditContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setOldContactInfo(ContactDetails.newBuilder().setValue(Util.EMAIL).build()) + .setEditedContactInfo(ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT) + .setValue(Util.EMAIL_EDITED).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.editContactInfoForAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_CONTACT_INFO_TYPE), + reply.getError().getCause().equals(Cause.MISSING_CONTACT_INFO_TYPE)); + } + + @Test + public void testEditContactInfoForAccountMissingContactInfoIdentifier() { + final EditContactInfoRequest request = EditContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setOldContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .setEditedContactInfo(ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).build()).build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.editContactInfoForAccount(request); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_CONTACT_INFO_IDENTIFIER), + reply.getError().getCause().equals(Cause.MISSING_CONTACT_INFO_IDENTIFIER)); + } + + @Test + public void testEditContactInfoForAccountInvalidPhone() { + final EditContactInfoRequest request = EditContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setOldContactInfo(ContactDetails.newBuilder().setType(ContactType.PHONE_CONTACT) + .setValue(Util.INVALID_PHONE_PROVIDER).setLabel("testLabel").build()) + .setEditedContactInfo(ContactDetails.newBuilder().setType(ContactType.PHONE_CONTACT) + .setValue(Util.PHONE_PROVIDER_EDITED).setLabel("testLabel").build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.editContactInfoForAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.PHONE_NUMBER_INVALID), + reply.getError().getCause().equals(Cause.PHONE_NUMBER_INVALID)); + } + + @Test + public void testEditContactInfoForAccountInvalidEmail() { + final EditContactInfoRequest request = EditContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setOldContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .setEditedContactInfo(ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT) + .setValue(Util.INVALID_EMAIL).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.editContactInfoForAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.EMAIL_INVALID), + reply.getError().getCause().equals(Cause.EMAIL_INVALID)); + } + + @Test + public void testEditContactInfoEmailForAccountErrorEditingContactInfo() { + final EditContactInfoRequest request = EditContactInfoRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID.toString()) + .setOldContactInfo( + ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT).setValue(Util.EMAIL).build()) + .setEditedContactInfo(ContactDetails.newBuilder().setType(ContactType.EMAIL_CONTACT) + .setValue(Util.EMAIL_EDITED).build()) + .build(); + given(accountRepositoryAdditional.addContactInfo(Mockito.any(UUID.class), Mockito.any(ContactInfo.class))) + .willReturn(false); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final StatusResponse reply = accountServiceBlockingStub.editContactInfoForAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_EDITING_CONTACT_INFO), + reply.getError().getCause().equals(Cause.ERROR_EDITING_CONTACT_INFO)); + } + + @Test + public void testUpdateAccountValidBirthdayDate() { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder().setAccountId(Util.ACCOUNT_ID.toString()) + .setFirstName(Util.FIRST_NAME).setBirthday(Date.newBuilder().setYear(Util.BIRTHDAY.getYear()) + .setMonth(Util.BIRTHDAY.getMonthValue()).setDay(Util.BIRTHDAY.getDayOfMonth()).build()) + .build(); + given(accountRepositoryAdditional.updateAccount(request)).willReturn(updatedAccount); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain birthday '%s'", Util.BIRTHDAY), + reply.getAccountDetails().getBirthday().getYear() == Util.BIRTHDAY.getYear()); + assertTrue(reply.getAccountDetails().getBirthday().getMonth() == Util.BIRTHDAY.getMonthValue()); + assertTrue(reply.getAccountDetails().getBirthday().getDay() == Util.BIRTHDAY.getDayOfMonth()); + } + + @Test + public void testUpdateAccountInvalidBirthdayDate() { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder().setAccountId(Util.ACCOUNT_ID.toString()) + .setFirstName(Util.FIRST_NAME) + .setBirthday(Date.newBuilder().setYear(1990).setMonth(9).setDay(32).build()).build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.INVALID_BIRTHDAY_DATE), + reply.getError().getCause().equals(Cause.INVALID_BIRTHDAY_DATE)); + } + + public void testSearchByUsername() throws ExecutionException, InterruptedException { + final GetByUsernameRequest request = GetByUsernameRequest.newBuilder().setUsername(Util.S_USERNAME).build(); + + AccountByUsername response = savedResponse; + + given(accountByUsernameRepository.findByUsername(request.getUsername())).willReturn(response); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByUsername(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain last name '%s'", Util.S_LAST_NAME.toString()), + reply.getSearchResultDetails().getLastName().equals(Util.S_LAST_NAME.toString())); + } + + @Test + public void testSearchByUsernameNotFound() throws ExecutionException, InterruptedException { + final GetByUsernameRequest request = GetByUsernameRequest.newBuilder().setUsername(Util.S_USERNAME).build(); + + AccountByUsername response = null; + + given(accountByUsernameRepository.findByUsername(request.getUsername())).willReturn(response); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByUsername(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.USERNAME_NOT_FOUND), + reply.getError().getCause().equals(Cause.USERNAME_NOT_FOUND)); + } + + @Test + public void testSearchByUsernameBadRequest() throws ExecutionException, InterruptedException { + final GetByUsernameRequest request = GetByUsernameRequest.newBuilder().build(); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByUsername(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MULTIPLE_INVALID_PARAMETERS), + reply.getError().getCause().equals(Cause.MULTIPLE_INVALID_PARAMETERS)); + } + + @Test + public void testSearchByUsernameInvalid() { + final GetByUsernameRequest request = GetByUsernameRequest.newBuilder().setUsername(Util.S_INVALID_USERNAME) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByUsername(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.USERNAME_INVALID), + reply.getError().getCause().equals(Cause.USERNAME_INVALID)); + } + + @Test + public void testSearchByPhoneNumber() throws ExecutionException, InterruptedException { + final GetByPhoneNumberRequest request = GetByPhoneNumberRequest.newBuilder().setPhoneNumber(Util.S_PHONE_NUMBER) + .build(); + + Optional response = Optional.of(savedAccount); + + given(accountProvider.getAccountByAuthenticationProvider(AuthenticationType.PHONE, request.getPhoneNumber())) + .willReturn(response); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByPhoneNumber(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain last name '%s'", savedAccount.getFirstName()), + reply.getSearchResultDetails().getLastName().equals(savedAccount.getLastName())); + } + + @Test + public void testSearchByPhoneNumberNotFound() throws ExecutionException, InterruptedException { + final GetByPhoneNumberRequest request = GetByPhoneNumberRequest.newBuilder().setPhoneNumber(Util.S_PHONE_NUMBER) + .build(); + + List response = new LinkedList<>(); + + given(accountByAuthenticationProviderRepository.findAllByAuthenticationProvider(request.getPhoneNumber())) + .willReturn(response); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByPhoneNumber(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.PHONENUMBER_NOT_FOUND), + reply.getError().getCause().equals(Cause.PHONENUMBER_NOT_FOUND)); + } + + @Test + public void testSearchByPhoneNumberBadRequest() throws ExecutionException, InterruptedException { + final GetByPhoneNumberRequest request = GetByPhoneNumberRequest.newBuilder().build(); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByPhoneNumber(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_PHONENUMBER), + reply.getError().getCause().equals(Cause.MISSING_PHONENUMBER)); + } + + @Test + public void testSearchByPhoneNumberInvalid() { + final GetByPhoneNumberRequest request = GetByPhoneNumberRequest.newBuilder() + .setPhoneNumber(Util.S_INVALID_PHONENUMBER).build(); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByPhoneNumber(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.INVALID_PHONENUMBER), + reply.getError().getCause().equals(Cause.INVALID_PHONENUMBER)); + } + + @Test + public void testSearchByEmail() throws ExecutionException, InterruptedException { + final GetByEmailRequest request = GetByEmailRequest.newBuilder().setEmail(Util.S_EMAIL).build(); + + Optional response = Optional.of(savedAccount); + + given(accountProvider.getAccountByAuthenticationProvider(AuthenticationType.EMAIL, request.getEmail())) + .willReturn(response); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByEmail(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain last name '%s'", savedAccount.getFirstName()), + reply.getSearchResultDetails().getLastName().equals(savedAccount.getLastName())); + } + + @Test + public void testSearchByEmailNotFound() throws ExecutionException, InterruptedException { + final GetByEmailRequest request = GetByEmailRequest.newBuilder().setEmail(Util.S_EMAIL).build(); + + List response = new LinkedList<>(); + + given(accountByAuthenticationProviderRepository.findAllByAuthenticationProvider(request.getEmail())) + .willReturn(response); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByEmail(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.EMAIL_NOT_FOUND), + reply.getError().getCause().equals(Cause.EMAIL_NOT_FOUND)); + } + + @Test + public void testSearchByEmailBadRequest() throws ExecutionException, InterruptedException { + final GetByEmailRequest request = GetByEmailRequest.newBuilder().build(); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByEmail(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_EMAIL), + reply.getError().getCause().equals(Cause.MISSING_EMAIL)); + } + + @Test + public void testSearchByEmailInvalid() { + final GetByEmailRequest request = GetByEmailRequest.newBuilder().setEmail(Util.S_INVALID_EMAIL).build(); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByEmail(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.EMAIL_INVALID), + reply.getError().getCause().equals(Cause.EMAIL_INVALID)); + } + + @Test + public void testSearchByQrCode() throws ExecutionException, InterruptedException { + final GetByQrCodeRequest request = GetByQrCodeRequest.newBuilder().setQrCode(Util.S_QR_CODE).build(); + + AccountByQrCode response = savedResponseQrCode; + + given(accountByQrCodeRepository.findByQrCode(request.getQrCode())).willReturn(response); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByQrCode(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain last name '%s'", Util.S_LAST_NAME.toString()), + reply.getSearchResultDetails().getLastName().equals(Util.S_LAST_NAME.toString())); + } + + @Test + public void testSearchByQrCodeNotFound() throws ExecutionException, InterruptedException { + final GetByQrCodeRequest request = GetByQrCodeRequest.newBuilder().setQrCode(Util.S_QR_CODE).build(); + + AccountByQrCode response = null; + + given(accountByQrCodeRepository.findByQrCode(request.getQrCode())).willReturn(response); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByQrCode(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.QR_CODE_NOT_FOUND), + reply.getError().getCause().equals(Cause.QR_CODE_NOT_FOUND)); + } + + @Test + public void testSearchByQrCodeBadRequest() throws ExecutionException, InterruptedException { + final GetByQrCodeRequest request = GetByQrCodeRequest.newBuilder().build(); + + final AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByQrCode(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_QR_CODE), + reply.getError().getCause().equals(Cause.MISSING_QR_CODE)); + } + +} diff --git a/src/test/java/biz/nynja/account/grpc/GrpcServerTestBase.java b/src/test/java/biz/nynja/account/utils/GrpcServerTestBase.java similarity index 94% rename from src/test/java/biz/nynja/account/grpc/GrpcServerTestBase.java rename to src/test/java/biz/nynja/account/utils/GrpcServerTestBase.java index d4b3507ab29b4838ea1b6c1bd09a0a6950aac871..e5e98cd59f919db81875a1112b1de81acde52d0c 100644 --- a/src/test/java/biz/nynja/account/grpc/GrpcServerTestBase.java +++ b/src/test/java/biz/nynja/account/utils/GrpcServerTestBase.java @@ -1,4 +1,8 @@ -package biz.nynja.account.grpc; +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ + +package biz.nynja.account.utils; import java.util.Optional; @@ -15,6 +19,10 @@ import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.inprocess.InProcessChannelBuilder; +/** + * gRPC test configurations. + */ + public abstract class GrpcServerTestBase { @Autowired(required = false) @@ -64,4 +72,4 @@ public abstract class GrpcServerTestBase { Optional.ofNullable(channel).ifPresent(ManagedChannel::shutdownNow); Optional.ofNullable(inProcChannel).ifPresent(ManagedChannel::shutdownNow); } -} +} \ No newline at end of file diff --git a/src/test/java/biz/nynja/account/utils/Util.java b/src/test/java/biz/nynja/account/utils/Util.java new file mode 100644 index 0000000000000000000000000000000000000000..11d213e1f912972c499dd77bcd81f2d58b997bbf --- /dev/null +++ b/src/test/java/biz/nynja/account/utils/Util.java @@ -0,0 +1,313 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ + +package biz.nynja.account.utils; + +import java.nio.ByteBuffer; +import java.time.LocalDate; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + + +import biz.nynja.account.grpc.AccessStatus; +import biz.nynja.account.grpc.Role; +import biz.nynja.account.models.Account; +import biz.nynja.account.models.AccountByAuthenticationProvider; +import biz.nynja.account.models.AccountByProfileId; +import biz.nynja.account.models.AccountByQrCode; +import biz.nynja.account.models.AccountByUsername; +import biz.nynja.account.models.AuthenticationProvider; +import biz.nynja.account.models.ContactInfo; +import biz.nynja.account.models.Profile; +import biz.nynja.account.models.ProfileByAuthenticationProvider; +import biz.nynja.account.models.PendingAccount; +import biz.nynja.account.models.PendingAccountByAuthenticationProvider; + +/** + * Unit tests variables, beans and help methods. + */ + +@TestConfiguration +public class Util { + + public static final UUID PROFILE_ID = UUID.fromString("12352345-e89b-43d3-d156-456732452200"); + public static final UUID ACCOUNT_ID = UUID.fromString("44532732-12b3-132d-e156-223732152200"); + public static final UUID ACCOUNT_ID_NOT_FOUND = UUID.fromString("44532732-12b3-132d-e156-223732152202"); + public static final UUID PROFILE_ID_NOT_FOUND = UUID.fromString("12352345-e89b-43d3-d156-456732452202"); + public static final String INVALID_ACCOUNT_ID = "111-222"; + public static final String ACCOUNT_MARK = "AccountMark"; + public static final String UPDATED_ACCOUNT_MARK = "PRIVATE"; + public static final String AUTHENTICATION_PROVIDER = "Provider"; + public static final String AUTHENTICATION_PROVIDER_TYPE = "ProviderType"; + public static final ByteBuffer AVATAR = ByteBuffer.allocate(42); + public static final String ACCOUNT_NAME = "AccountName"; + public static final String ACCOUNT_STATUS = "active"; + public static final long CREATION_TIMESTAMP = 45; + public static final long EXISTING_PENDING_ACCOUNT_TIMESTAMP_UPDATED = 65; + public static final long LAST_UPDATE_TIMESTAMP = 80; + public static final Set CONTACTS_INFO = new HashSet(); + public static final String QR_CODE = "QRcode"; + public static final String EMAIL = "email@test.com"; + public static final String EMAIL_EDITED = "test@mytestemail.com"; + public static final String INVALID_EMAIL = "invalidemail@"; + public static final String USERNAME = "jdoe"; + public static final String PASSWORD = "abc123"; + public static final String FIRST_NAME = "John"; + public static final String LAST_NAME = "Doe"; + public static final LocalDate BIRTHDAY = LocalDate.of(1990, 9, 16); + public static final String MIDDLE_NAME = "Kass"; + public static final String PHONE_NUMBER = "+359887123456"; + public static final String PHONE_NUMBER_STREIGHT = "359887123456"; + public static final String PHONE_PROVIDER = "BG:+359887123456"; + public static final String PHONE_PROVIDER_EDITED = "BG:+359887111111"; + public static final String INVALID_PHONE_PROVIDER = "BG:+3"; + public static final String PHONE_TYPE = "PHONE"; + public static final String EMAIL_TYPE = "EMAIL"; + public static final String FACBOOK_TYPE = "FACEBOOK"; + public static final String GOOGLEPLUS_TYPE = "GOOGLEPLUS"; + + public static final String S_USERNAME = "TomJohns"; + public static final String S_PHONE_NUMBER = "359878123456"; + public static final String S_EMAIL = "p_petrov@abv.bg"; + public static final String S_QR_CODE = "QRcoded"; + public static final String S_INVALID_USERNAME = "Tom-Johns"; + public static final String S_INVALID_PHONENUMBER = "GB:44887359883"; // one DTMF less + public static final String S_INVALID_EMAIL = "p_petrov.@abv.bg"; + public static final String S_AVATAR_STRING = "avatar-TomJohns"; + public static final String S_FIRST_NAME = "Tom"; + public static final String S_LAST_NAME = "Johns"; + + + @Bean + public ProfileByAuthenticationProvider profileByAuthenticationProvider() { + ProfileByAuthenticationProvider profile = new ProfileByAuthenticationProvider(); + profile.setProfileId(PROFILE_ID); + profile.setAuthenticationProvider(PHONE_PROVIDER); + profile.setAuthenticationProviderType(PHONE_TYPE); + return profile; + } + + @Bean + public Profile profile1AuthProvider() { + Set aps = new HashSet<>(); + AuthenticationProvider ap1 = new AuthenticationProvider(); + ap1.setType(PHONE_TYPE); + ap1.setValue(PHONE_PROVIDER); + aps.add(ap1); + Profile profile = new Profile(); + profile.setProfileId(PROFILE_ID); + profile.setPasscode("123456"); + profile.setAuthenticationProviders(aps); + return profile; + } + + @Bean + public Profile profile2AuthProviders() { + Set aps = new HashSet<>(); + AuthenticationProvider ap1 = new AuthenticationProvider(); + ap1.setType(PHONE_TYPE); + ap1.setValue(PHONE_PROVIDER); + aps.add(ap1); + AuthenticationProvider ap2 = new AuthenticationProvider(); + ap2.setType(EMAIL_TYPE); + ap2.setValue(EMAIL); + aps.add(ap2); + Profile profile = new Profile(); + profile.setProfileId(PROFILE_ID); + profile.setPasscode("123456"); + profile.setAuthenticationProviders(aps); + return profile; + } + + @Bean + public Account phoneAccount() { + Account account = new Account(); + account.setAccountId(ACCOUNT_ID); + account.setAuthenticationProvider(PHONE_PROVIDER); + account.setAuthenticationProviderType(PHONE_TYPE); + return account; + } + + @Bean + public Account newAccount() { + Account account = new Account(); + account.setUsername(USERNAME); + account.setFirstName(FIRST_NAME); + account.setLastName(LAST_NAME); + account.setUsername(USERNAME); + account.setFirstName(FIRST_NAME); + account.setLastName(LAST_NAME); + account.setAuthenticationProvider(EMAIL); + account.setAuthenticationProviderType(EMAIL_TYPE); + account.setAccessStatus(AccessStatus.ENABLED.toString()); + return account; + } + + @Bean + public AccountByProfileId savedAccountByProfileId() { + AccountByProfileId account = new AccountByProfileId(); + account.setAccountId(ACCOUNT_ID); + account.setProfileId(PROFILE_ID); + account.setAccountMark(ACCOUNT_MARK); + account.setAuthenticationProvider(AUTHENTICATION_PROVIDER); + account.setAuthenticationProviderType(AUTHENTICATION_PROVIDER_TYPE); + account.setFirstName(FIRST_NAME); + account.setLastName(LAST_NAME); + account.setAvatar(AVATAR); + account.setAccountName(ACCOUNT_NAME); + account.setUsername(USERNAME); + account.setCreationTimestamp(CREATION_TIMESTAMP); + account.setLastUpdateTimestamp(LAST_UPDATE_TIMESTAMP); + account.setContactsInfo(CONTACTS_INFO); + account.setQrCode(QR_CODE); + account.setRoles(Set.of(Role.USER.toString())); + account.setAccessStatus(AccessStatus.ENABLED.toString()); + return account; + } + + @Bean + public Account savedAccount() { + Account account = new Account(); + account.setAccountId(ACCOUNT_ID); + account.setProfileId(PROFILE_ID); + account.setAuthenticationProvider(EMAIL); + account.setAuthenticationProviderType(EMAIL_TYPE); + account.setUsername(USERNAME); + account.setFirstName(FIRST_NAME); + account.setLastName(LAST_NAME); + + return account; + } + + @Bean + public Account updatedAccount() { + Account account = new Account(); + account.setAccountId(ACCOUNT_ID); + account.setProfileId(PROFILE_ID); + account.setUsername(USERNAME); + account.setFirstName(FIRST_NAME); + account.setLastName(LAST_NAME); + account.setBirthday(BIRTHDAY); + account.setAccountMark(UPDATED_ACCOUNT_MARK); + return account; + } + + @Bean + public Profile updatedProfile() { + Profile profile = new Profile(); + profile.setProfileId(PROFILE_ID); + Set authProviders = new HashSet(); + authProviders.add(AuthenticationProvider.createAuthenticationProviderFromStrings(PHONE_TYPE, PHONE_NUMBER)); + profile.setAuthenticationProviders(authProviders); + return profile; + } + + @Bean + public AccountByAuthenticationProvider accountByPhone() { + AccountByAuthenticationProvider accountByPhone = new AccountByAuthenticationProvider(); + accountByPhone.setAuthenticationProvider(PHONE_NUMBER); + accountByPhone.setAuthenticationProviderType(PHONE_TYPE); + accountByPhone.setAccountId(ACCOUNT_ID); + accountByPhone.setProfileId(PROFILE_ID); + accountByPhone.setUsername(USERNAME); + accountByPhone.setFirstName(FIRST_NAME); + accountByPhone.setLastName(LAST_NAME); + return accountByPhone; + } + + @Bean + public AccountByAuthenticationProvider accountByEmail() { + AccountByAuthenticationProvider accountByPhone = new AccountByAuthenticationProvider(); + accountByPhone.setAuthenticationProvider(EMAIL); + accountByPhone.setAuthenticationProviderType(EMAIL_TYPE); + accountByPhone.setAccountId(ACCOUNT_ID); + accountByPhone.setProfileId(PROFILE_ID); + accountByPhone.setUsername(USERNAME); + accountByPhone.setFirstName(FIRST_NAME); + accountByPhone.setLastName(LAST_NAME); + return accountByPhone; + } + + @Bean + public AccountByAuthenticationProvider accountByFacebook() { + AccountByAuthenticationProvider accountByPhone = new AccountByAuthenticationProvider(); + accountByPhone.setAuthenticationProvider(EMAIL); + accountByPhone.setAuthenticationProviderType(FACBOOK_TYPE); + accountByPhone.setAccountId(ACCOUNT_ID); + accountByPhone.setProfileId(PROFILE_ID); + accountByPhone.setUsername(USERNAME); + accountByPhone.setFirstName(FIRST_NAME); + accountByPhone.setLastName(LAST_NAME); + return accountByPhone; + } + + @Bean + public PendingAccount savedPendingAccount() { + PendingAccount account = new PendingAccount(); + account.setAccountId(ACCOUNT_ID); + account.setProfileId(PROFILE_ID); + account.setAuthenticationProvider(EMAIL); + account.setAuthenticationProviderType(EMAIL_TYPE); + account.setCreationTimestamp(CREATION_TIMESTAMP); + return account; + } + + @Bean + public PendingAccount existingPendingAccount() { + PendingAccount pendingAccount = new PendingAccount(); + pendingAccount.setAccountId(ACCOUNT_ID); + pendingAccount.setProfileId(PROFILE_ID); + pendingAccount.setAuthenticationProvider(EMAIL); + pendingAccount.setAuthenticationProviderType(EMAIL_TYPE); + pendingAccount.setCreationTimestamp(EXISTING_PENDING_ACCOUNT_TIMESTAMP_UPDATED); + return pendingAccount; + } + + @Bean + public PendingAccountByAuthenticationProvider existingPendingAccountByAuthenticationProvider() { + PendingAccountByAuthenticationProvider pendingAccount = new PendingAccountByAuthenticationProvider(); + pendingAccount.setAccountId(ACCOUNT_ID); + pendingAccount.setProfileId(PROFILE_ID); + pendingAccount.setAuthenticationProvider(EMAIL); + pendingAccount.setAuthenticationProviderType(EMAIL_TYPE); + pendingAccount.setCreationTimestamp(EXISTING_PENDING_ACCOUNT_TIMESTAMP_UPDATED); + return pendingAccount; + } + + @Bean + public AccountByUsername savedResponse() { + AccountByUsername response = new AccountByUsername(); + response.setAccountId(Util.ACCOUNT_ID); + response.setAvatar(ByteBuffer.wrap(S_AVATAR_STRING.getBytes())); + response.setFirstName(Util.S_FIRST_NAME); + response.setLastName(Util.S_LAST_NAME); + return response; + } + + @Bean + public AccountByAuthenticationProvider savedResponseProvider() { + AccountByAuthenticationProvider response = new AccountByAuthenticationProvider(); + response.setAccountId(Util.ACCOUNT_ID); + response.setAvatar(ByteBuffer.wrap(S_AVATAR_STRING.getBytes())); + response.setFirstName(Util.S_FIRST_NAME); + response.setLastName(Util.S_LAST_NAME); + return response; + } + + @Bean + public AccountByQrCode savedResponseQrCode() { + AccountByQrCode response = new AccountByQrCode(); + response.setAccountId(Util.ACCOUNT_ID); + response.setAvatar(ByteBuffer.wrap(S_AVATAR_STRING.getBytes())); + response.setFirstName(Util.S_FIRST_NAME); + response.setLastName(Util.S_LAST_NAME); + response.setQrCode(Util.S_QR_CODE); + return response; + } + +}