diff --git a/.classpath b/.classpath deleted file mode 100644 index f0b7e81..0000000 --- a/.classpath +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..097f9f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + diff --git a/.github/codeql.yml b/.github/codeql.yml new file mode 100644 index 0000000..9771ca0 --- /dev/null +++ b/.github/codeql.yml @@ -0,0 +1,52 @@ +name: "Code scanning - action" + +on: + push: + pull_request: + schedule: + - cron: '0 19 * * 0' + +jobs: + CodeQL-Build: + + # CodeQL runs on ubuntu-latest and windows-latest + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + # Override language selection by uncommenting this and choosing your languages + # with: + # languages: go, javascript, csharp, python, cpp, java + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2659fed --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +version: 2 +updates: +- package-ecosystem: docker + directory: "/" + schedule: + interval: weekly + time: '11:00' + open-pull-requests-limit: 10 +- package-ecosystem: pip + directory: "/" + schedule: + interval: daily + time: '11:00' + open-pull-requests-limit: 10 diff --git a/.github/workflows/manual-build.yml b/.github/workflows/manual-build.yml new file mode 100644 index 0000000..944f903 --- /dev/null +++ b/.github/workflows/manual-build.yml @@ -0,0 +1,11 @@ +--- +name: Manual Build & Push +on: + workflow_dispatch: +jobs: + build-push: + uses: kbase/.github/.github/workflows/reusable_build-push.yml@main + with: + name: '${{ github.event.repository.name }}-develop' + tags: br-${{ github.ref_name }} + secrets: inherit diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml new file mode 100644 index 0000000..0fa1c46 --- /dev/null +++ b/.github/workflows/pr_build.yml @@ -0,0 +1,43 @@ +--- +name: Pull Request Build, Tag, & Push +on: + pull_request: + branches: + - develop + - main + - master + types: + - opened + - reopened + - synchronize + - closed +jobs: + build-develop-open: + if: github.base_ref == 'develop' && github.event.pull_request.merged == false + uses: kbase/.github/.github/workflows/reusable_build.yml@main + secrets: inherit + build-develop-merge: + if: github.base_ref == 'develop' && github.event.pull_request.merged == true + uses: kbase/.github/.github/workflows/reusable_build-push.yml@main + with: + name: '${{ github.event.repository.name }}-develop' + tags: pr-${{ github.event.number }},latest + secrets: inherit + build-main-open: + if: (github.base_ref == 'main' || github.base_ref == 'master') && github.event.pull_request.merged == false + uses: kbase/.github/.github/workflows/reusable_build-push.yml@main + with: + name: '${{ github.event.repository.name }}' + tags: pr-${{ github.event.number }} + secrets: inherit + build-main-merge: + if: (github.base_ref == 'main' || github.base_ref == 'master') && github.event.pull_request.merged == true + uses: kbase/.github/.github/workflows/reusable_build-push.yml@main + with: + name: '${{ github.event.repository.name }}' + tags: pr-${{ github.event.number }},latest-rc + secrets: inherit + trivy-scans: + if: (github.base_ref == 'develop' || github.base_ref == 'main' || github.base_ref == 'master' ) && github.event.pull_request.merged == false + uses: kbase/.github/.github/workflows/reusable_trivy-scans.yml@main + secrets: inherit diff --git a/.github/workflows/release-main.yml b/.github/workflows/release-main.yml new file mode 100644 index 0000000..a254678 --- /dev/null +++ b/.github/workflows/release-main.yml @@ -0,0 +1,25 @@ +--- +name: Release - Build & Push Image +on: + release: + branches: + - main + - master + types: [ published ] +jobs: + check-source-branch: + uses: kbase/.github/.github/workflows/reusable_validate-branch.yml@main + with: + build_branch: '${{ github.event.release.target_commitish }}' + validate-release-tag: + needs: check-source-branch + uses: kbase/.github/.github/workflows/reusable_validate-release-tag.yml@main + with: + release_tag: '${{ github.event.release.tag_name }}' + build-push: + needs: validate-release-tag + uses: kbase/.github/.github/workflows/reusable_build-push.yml@main + with: + name: '${{ github.event.repository.name }}' + tags: '${{ github.event.release.tag_name }},latest' + secrets: inherit diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..c85d28d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,70 @@ +name: KBase User Profile tests + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - ready_for_review + push: + # run workflow when merging to main or develop + branches: + - main + - master + - develop + +jobs: + + user_profile_tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - java: '8' + mongo: 'mongodb-linux-x86_64-ubuntu2204-7.0.4' + - java: '11' + mongo: 'mongodb-linux-x86_64-3.6.23' + steps: + - uses: actions/checkout@v3 + + - name: Set up java + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: ${{matrix.java}} + + - name: Install dependencies and set up test config + env: + KBASE_CI_TOKEN: ${{ secrets.KBASE_CI_TOKEN }} + KBASE_CI_TOKEN2: ${{ secrets.KBASE_CI_TOKEN2 }} + shell: bash + run: | + export HOMEDIR=`pwd` + + # move to parent dir of homedir to install binaries etc + cd .. + + # set up mongo + wget -q http://fastdl.mongodb.org/linux/${{matrix.mongo}}.tgz + tar xfz ${{matrix.mongo}}.tgz + export MONGOD=`pwd`/${{matrix.mongo}}/bin/mongod + + # set up test config + cd $HOMEDIR + cp -n test.cfg.example test.cfg + sed -i "s#^test.mongo-exe-path.*#test.mongo-exe-path=$MONGOD#" test.cfg + sed -i "s#^test.admin-token.*#test.admin-token = $KBASE_CI_TOKEN#" test.cfg + sed -i "s#^test.usr1-token.*#test.usr1-token = $KBASE_CI_TOKEN2#" test.cfg + echo "\ntest.mongo-exe-path=$MONGOD\n" >> test.cfg + + - name: Run tests + shell: bash + run: ./gradlew test + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true diff --git a/.gitignore b/.gitignore index 9637350..b3724f8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,13 @@ classes dist /build/ /bin/ +/.settings/ +/test.cfg +/.pydevproject + +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build +/.classpath diff --git a/.project b/.project index fe3227b..03ea2e7 100644 --- a/.project +++ b/.project @@ -10,8 +10,30 @@ + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + org.python.pydev.PyDevBuilder + + + org.eclipse.jdt.core.javanature + org.python.pydev.pythonNature + org.eclipse.buildship.core.gradleprojectnature diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ee655de..0000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -dist: trusty -sudo: required -language: java -jdk: - - openjdk8 -before_install: - - sudo apt-get -qq update - - sudo apt-get install -y ant-optional - - sudo pip install sphinx -env: - global: # These are the encrypted DOCKER_USER and DOCKER_PASS env vars - - secure: "O3Uv6C+Y7gywdNuLN73KsoIhZ+XV+lCN29KnImSCGHnn/Oy6cZnT6CKQrEpaCkXBnQ6A8UijO06MQXcMAy2yj1SIXud43Y0wjcUDvkUFLijAVcOdLSsIww6E062kLBrkW98rEg9iuVmb6z59G7Wbz9eAeaq4IMxR7izGAIPvi+s=" - - secure: "Es4pqoqmVtVrNq2SreoJ20pLV6PeDDE4WddG5KeFB3aleu5C8wHMvD8GMKjNCOK6zsLAQYR5AeIYo0RjOEGsE83gjqf9StP/5vfn/kQa4H7867NohMjrMHdKtLSqHKky0DYK36x9e6LklMHVTxsWuBTgwJm3e1lD9ktKIC9fudg=" -script: - - pushd .. - - git clone https://github.com/kbase/jars - - popd - - make build-libs diff --git a/DEPENDENCIES b/DEPENDENCIES deleted file mode 100644 index 2723de6..0000000 --- a/DEPENDENCIES +++ /dev/null @@ -1 +0,0 @@ -jars diff --git a/Dockerfile b/Dockerfile index bec72d3..4b2179c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,26 +1,35 @@ -FROM kbase/kb_jre:latest AS build -# Multistage Build Setup -RUN apt-get -y update && apt-get -y install ant git openjdk-8-jdk -RUN cd /tmp && \ - git clone https://github.com/kbase/jars - -COPY . /tmp/user_profile - -# Bypass the makefile, which has perl dependencies, and just use -# the underlying ant compile and ant buildwar -RUN cd /tmp/user_profile && \ - ant compile && \ - ant buildwar +FROM kbase/sdkbase2:latest AS build + +WORKDIR /tmp/up + +# dependencies take a while to D/L, so D/L & cache before the build so code changes don't cause +# a new D/L +# can't glob *gradle because of the .gradle dir +COPY build.gradle gradlew settings.gradle /tmp/up/ +COPY gradle/ /tmp/up/gradle/ +RUN ./gradlew dependencies + +# Now build the code +# copy the deployment dir first since it's unlikely to change often +COPY deployment/ /kb/deployment +COPY jettybase /kb/deployment/jettybase +COPY src /tmp/up/src/ +COPY war /tmp/up/war/ +RUN ./gradlew war + +# Build the deployment directory +ENV DEPL=/kb/deployment/jettybase +RUN mkdir -p $DEPL/webapps +RUN cp /tmp/up/build/libs/user_profile.war $DEPL/webapps/ROOT.war FROM kbase/kb_jre:latest + # These ARGs values are passed in via the docker build command ARG BUILD_DATE ARG VCS_REF ARG BRANCH=develop -COPY deployment/ /kb/deployment/ -COPY jettybase/ /kb/deployment/jettybase/ -COPY --from=build /tmp/user_profile/dist/UserProfileService.war /kb/deployment/jettybase/webapps/ROOT.war +COPY --from=build /kb/deployment/ /kb/deployment/ # The BUILD_DATE value seem to bust the docker cache when the timestamp changes, move to # the end diff --git a/LICENSE.md b/LICENSE.md index fb2bc8a..e5b3fb9 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2014 The KBase Project and its Contributors +Copyright (c) 2014-present The KBase Project and its Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/Makefile b/Makefile deleted file mode 100644 index b071975..0000000 --- a/Makefile +++ /dev/null @@ -1,164 +0,0 @@ -#port is now set in deploy.cfg -SERVICE_PORT = $(shell perl server_scripts/get_deploy_cfg.pm UserProfile.port) -SERVICE = user_profile -SERVICE_CAPS = UserProfile -SPEC_FILE = UserProfile.spec -CLIENT_JAR = UserProfileClient.jar -WAR = UserProfileService.war -URL = https://kbase.us/services/user_profile/rpc -DEFAULT_SCRIPT_URL = $(URL) - -#End of user defined variables - -GITCOMMIT := $(shell git rev-parse --short HEAD) -TAGS := $(shell git tag --contains $(GITCOMMIT)) -BRANCH := $(shell git symbolic-ref --short HEAD) -TOP_DIR = $(shell python -c "import os.path as p; print p.abspath('../..')") - -TOP_DIR_NAME = $(shell basename $(TOP_DIR)) - -DIR = $(shell pwd) - -ifeq ($(TOP_DIR_NAME), dev_container) -include $(TOP_DIR)/tools/Makefile.common -endif - -DEPLOY_RUNTIME ?= /kb/runtime -JAVA_HOME ?= $(DEPLOY_RUNTIME)/java -TARGET ?= /kb/deployment -SERVICE_DIR ?= $(TARGET)/services/$(SERVICE) -GLASSFISH_HOME ?= $(DEPLOY_RUNTIME)/glassfish3 -SERVICE_USER ?= kbase - -ASADMIN = $(GLASSFISH_HOME)/glassfish/bin/asadmin - -ANT = ant - - -SRC_PERL = $(wildcard scripts/*.pl) -BIN_PERL = $(addprefix $(BIN_DIR)/,$(basename $(notdir $(SRC_PERL)))) - -# make sure our make test works -.PHONY : test - -default: build-bin build-docs build-bin - -# fake deploy-cfg target for when this is run outside the dev_container -deploy-cfg: - - - -SCRIPTBINDESTINATION = $(DIR)/bin -ifeq ($(TOP_DIR_NAME), dev_container) -include $(TOP_DIR)/tools/Makefile.common.rules -SCRIPTBINDESTINATION = $(TOP_DIR)/bin -endif - -build-libs: - $(ANT) compile - -build-bin: $(BIN_PERL) - -build-docs: build-libs - mkdir -p docs - @#$(ANT) javadoc - pod2html --infile=lib/Bio/KBase/$(SERVICE_CAPS)/Client.pm --outfile=docs/$(SERVICE_CAPS).html - rm -f pod2htm?.tmp - cp $(SPEC_FILE) docs/. - -compile: compile-typespec compile-typespec-java - -compile-java-client: - @# $(ANT) compile_client - -compile-typespec-java: - kb-sdk compile $(SPEC_FILE) \ - --out . \ - --java \ - --javasrc src \ - --javapackage us.kbase \ - --javasrv \ - --url $(URL) - -compile-typespec: - kb-sdk compile $(SPEC_FILE) \ - --plclname Bio::KBase::$(SERVICE_CAPS)::Client \ - --pyclname biokbase.$(SERVICE).client \ - --jsclname javascript/$(SERVICE_CAPS)/Client \ - --out lib \ - --url $(URL) - -test: test-client test-service test-scripts - -test-client: test-service - @# $(ANT) test_client_import - -test-service: - test/cfg_to_runner.py $(TESTCFG) - test/run_tests.sh - -test-scripts: - - -deploy: deploy-client deploy-service - -deploy-client: deploy-client-libs deploy-docs deploy-perl-scripts - -deploy-client-libs: - mkdir -p $(TARGET)/lib/ - cp dist/client/$(CLIENT_JAR) $(TARGET)/lib/ - cp -rv lib/* $(TARGET)/lib/ - echo $(GITCOMMIT) > $(TARGET)/lib/$(SERVICE).clientdist - echo $(TAGS) >> $(TARGET)/lib/$(SERVICE).clientdist - -deploy-docs: - mkdir -p $(SERVICE_DIR)/webroot - cp -r docs/* $(SERVICE_DIR)/webroot/. - -deploy-service: deploy-service-libs deploy-service-scripts deploy-cfg - -deploy-service-libs: - $(ANT) buildwar - mkdir -p $(SERVICE_DIR) - cp dist/$(WAR) $(SERVICE_DIR) - mkdir $(SERVICE_DIR)/webapps - cp dist/$(WAR) $(SERVICE_DIR)/webapps/root.war - echo $(GITCOMMIT) > $(SERVICE_DIR)/$(SERVICE).serverdist - echo $(TAGS) >> $(SERVICE_DIR)/$(SERVICE).serverdist - -deploy-service-scripts: - cp server_scripts/glassfish_administer_service.py $(SERVICE_DIR) - cp server_scripts/jetty.xml $(SERVICE_DIR) - server_scripts/build_server_control_scripts.py $(SERVICE_DIR) $(WAR)\ - $(TARGET) $(JAVA_HOME) deploy.cfg $(ASADMIN) $(SERVICE_CAPS)\ - $(SERVICE_PORT) - -deploy-upstart: - echo "# $(SERVICE) service" > /etc/init/$(SERVICE).conf - echo "# NOTE: stop $(SERVICE) does not work" >> /etc/init/$(SERVICE).conf - echo "# Use the standard stop_service script as the $(SERVICE_USER) user" >> /etc/init/$(SERVICE).conf - echo "#" >> /etc/init/$(SERVICE).conf - echo "# Make sure to set up the $(SERVICE_USER) user account" >> /etc/init/$(SERVICE).conf - echo "# shell> groupadd kbase" >> /etc/init/$(SERVICE).conf - echo "# shell> useradd -r -g $(SERVICE_USER) $(SERVICE_USER)" >> /etc/init/$(SERVICE).conf - echo "#" >> /etc/init/$(SERVICE).conf - echo "start on runlevel [23]" >> /etc/init/$(SERVICE).conf - echo "stop on runlevel [!23]" >> /etc/init/$(SERVICE).conf - echo "pre-start exec chown -R $(SERVICE_USER) $(TARGET)/services/$(SERVICE)" >> /etc/init/$(SERVICE).conf - echo "exec su kbase -c '$(TARGET)/services/$(SERVICE)/start_service'" >> /etc/init/$(SERVICE).conf - -docker_image: - IMAGE_NAME=kbase/user_profile:$(BRANCH) bash -x hooks/build - -undeploy: - -rm -rf $(SERVICE_DIR) - -rm -rfv $(TARGET)/lib/Bio/KBase/$(SERVICE) - -rm -rfv $(TARGET)/lib/biokbase/$(SERVICE) - -rm -rfv $(TARGET)/lib/javascript/$(SERVICE) - -rm -rfv $(TARGET)/lib/$(CLIENT_JAR) - -clean: - $(ANT) clean - -rm -rf docs - -rm -rf bin - @#TODO remove lib once files are generated on the fly diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index 493ff02..7291dd2 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -1,21 +1,27 @@ -USER PROFILE SERVICE ------------------------------------------ +# USER PROFILE SERVICE + A lightweight service to store public KBase user profiles and related basic information. -VERSION: 0.2.1 (Released 4/1/19) ------------------------------- +## VERSION: 0.3.0 (Released 10/9/24) + +* The MongoDB clients have been updated to the most recent version. +* Added the ``mongo-retrywrites`` configuration setting in ``deploy.cfg``, defaulting to ``false``. +* Switched the build system from Make / Ant to Gradle. +* The `lookup_globus_user` method was removed. +* The `globus-url` configuration parameter was removed. -- Updated the MongoDB client to 3.10.1, allowing for Mongo 3.x compatibility. +## VERSION: 0.2.1 (Released 4/1/19) + +* Updated the MongoDB client to 3.10.1, allowing for Mongo 3.x compatibility. As such, the mongodb-retry deploy.cfg parameter has been removed, as the MongoDB client now handles that internally. -VERSION: 0.1.X to 0.2.0 ------------------------ +## VERSION: 0.1.X to 0.2.0 * The release notes for versions between 0.1.0 and 0.2.1 are lost. -VERSION: 0.1.0 (Released several years ago, now being Feb 2019) ---------------------------------------------------------------- +## VERSION: 0.1.0 (Released several years ago, now being Feb 2019) + NEW FEATURES: - This is the first release of the User Profile service. All features are new. diff --git a/UserProfile.spec b/UserProfile.spec index 843ab77..4c75109 100644 --- a/UserProfile.spec +++ b/UserProfile.spec @@ -67,14 +67,5 @@ module UserProfile { todo: add some way to remove fields. Fields in profile can only be modified or added. */ funcdef update_user_profile(SetUserProfileParams p) returns () authentication required; - - - typedef structure { - string email; - string fullName; - string userName; - } GlobusUser; - - funcdef lookup_globus_user(list usernames) returns (mapping users) authentication required; }; diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..dd9e01d --- /dev/null +++ b/build.gradle @@ -0,0 +1,185 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +plugins { + id 'java' + id 'war' + id 'jacoco' +} + +var VER_AUTH2_CLIENT = "0.5.0" +var VER_JAVA_COMMON = "0.3.0" + +var DEFAULT_URL = "https://ci.kbase.us/services/user_profile/rpc" + +var LOC_UP_SPEC = "$rootDir/UserProfile.spec" +var LOC_DOC_HTML = "$rootDir/docshtml" + +repositories { + mavenCentral() + maven { + name = "JitPack" + url = 'https://jitpack.io' + } + maven { + name = "Clojars" + url = "https://repo.clojars.org/" + } +} +compileJava { + // TODO BUILD remove when we no longer support java 8, use `options.release = 11` if needed + java.sourceCompatibility = JavaVersion.VERSION_1_8 + java.targetCompatibility = JavaVersion.VERSION_1_8 +} + +javadoc { + /* We don't actually need the full javadoc for anything, so just hijack this task for + * building the client javadocs. If we ever need the full javadocs make a new task for the + * client java docs + */ + options { + // I don't know why this isn't working, but it's not worth spending time on right now + links "https://docs.oracle.com/javase/8/docs/api/" + links "https://javadoc.jitpack.io/com/github/kbase/auth2_client_java/$VER_AUTH2_CLIENT/javadoc/" + links "https://javadoc.jitpack.io/com/github/kbase/java_common/$VER_JAVA_COMMON/javadoc/" + } + exclude "**/userprofile/MongoController.java" + exclude "**/userprofile/UserProfileServer.java" +} + +test { + systemProperty "test.cfg", "./test.cfg" + testLogging { + exceptionFormat = 'full' + showStandardStreams = true + } + finalizedBy jacocoTestReport +} + +// TODO TEST add a test that starts the server in a docker container and checks some simple cmds + +jacocoTestReport { + reports { + xml.required = true + csv.required = true + } +} + +jar { + // Much like javadoc, we hijack this task to build the client jar, since we don't need + // a service jar + exclude "us/kbase/userprofile/UserProfileServer*.class" // exclude anonymous classes + exclude "us/kbase/userprofile/MongoController*.class" // exclude anonymous classes + archiveAppendix = 'client' +} + +war { + webXml = file('war/web.xml') +} + +/* SDK compile notes: + * kb-sdk starts a docker container in interactive mode. Gradle's commandLine doesn't allocate + * a tty so the command fails. + * I tried using a ProcessBuilder and + * https://docs.oracle.com/en%2Fjava%2Fjavase%2F11%2Fdocs%2Fapi%2F%2F/java.base/java/lang/ProcessBuilder.html#inheritIO() + * but that failed also with no useful output. + * + * The current solution is to precede the kb-sdk call with a script call, which either + * allocates a tty or fools kb-sdk into thinking there is one - not quite sure. + * https://man7.org/linux/man-pages/man1/script.1.html + * I tried to redirect the script log file to /dev/null with -O and --log-out but script didn't + * recognize either option, hence the delete. + * + * This is, generally speaking, a janky mess and if someone can find a better way to do this + * that'd be fantastic. + */ + + var LOC_SCRIPT_TYPESCRIPT = "$rootDir/typescript" + +// TODO GRADLE is there some way to DRY these 3 compile tasks up? Not a huge deal +task sdkCompileHTML { + // We want to check in the HTML so we don't put it in the build dir + var cmd = "kb-sdk compile --html --out $LOC_DOC_HTML $LOC_UP_SPEC" + doLast { + exec { + commandLine "script", "-qefc", cmd + } + delete LOC_SCRIPT_TYPESCRIPT + } +} + +task sdkCompileLibs { + var cmd = "kb-sdk compile " + + "--out lib " + + "--jsclname javascript/UserProfile/Client " + + "--plclname Bio::KBase::UserProfile::Client " + + "--pyclname biokbase.user_profile.client " + + "--url $DEFAULT_URL " + + LOC_UP_SPEC + doLast { + exec { + commandLine "script", "-qefc", cmd + } + delete LOC_SCRIPT_TYPESCRIPT + delete "$rootDir/lib/biokbase/user_profile/authclient.py" + } +} + +task sdkCompileJava { + // TODO GRADLE is there a variable for src/main/java? + var cmd = "kb-sdk compile " + + "--java " + + "--javasrc $rootDir/src/main/java/ " + + "--javasrv " + + "--out . " + + "--url $DEFAULT_URL " + + LOC_UP_SPEC + doLast { + exec { + commandLine "script", "-qefc", cmd + } + delete LOC_SCRIPT_TYPESCRIPT + } +} + +task sdkCompile { + dependsOn sdkCompileHTML + dependsOn sdkCompileJava + dependsOn sdkCompileLibs +} + +configurations { + // can't directly access testImplementation, so extend and access + testimpl.extendsFrom testImplementation +} + +dependencies { + + implementation 'org.ini4j:ini4j:0.5.2' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.2.3' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.2.3' + implementation "com.github.kbase:java_common:$VER_JAVA_COMMON" + implementation "com.github.kbase:auth2_client_java:$VER_AUTH2_CLIENT" + implementation 'javax.annotation:javax.annotation-api:1.2' + implementation 'javax.servlet:servlet-api:2.5' + implementation 'org.mongodb:mongodb-driver-core:4.11.1' + implementation 'org.mongodb:mongodb-driver-sync:4.11.1' + implementation 'org.mongodb:bson-record-codec:4.11.1' + implementation 'org.mongodb:bson:4.11.1' + implementation 'com.google.guava:guava:31.1-jre' + + testImplementation 'junit:junit:4.9' + testImplementation 'commons-io:commons-io:2.4' + testImplementation('com.github.kbase:java_test_utilities:0.1.0') { + exclude group: 'com.fasterxml.jackson.core' // upgrading breaks stuff + } + +} + +task showTestClassPath { + doLast { + configurations.testimpl.each { println it } + } +} + diff --git a/build.xml b/build.xml deleted file mode 100644 index d486d91..0000000 --- a/build.xml +++ /dev/null @@ -1,240 +0,0 @@ - - - - Build file for the User Profile - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/deploy.cfg b/deploy.cfg index 7d09480..0e07b07 100644 --- a/deploy.cfg +++ b/deploy.cfg @@ -21,6 +21,10 @@ mongodb-database = user_profile_db # password for the account #mongodb-pwd = add password here +# Whether to enable ('true') the MongoDB retryWrites parameter or not (anything other than 'true'). +# See https://www.mongodb.com/docs/manual/core/retryable-writes/ +mongodb-retrywrites=false + # the user name for an administrator admin = kbuserprofileadmin @@ -37,5 +41,4 @@ min-memory = 1000 #Maximum memory size in MB. max-memory = 2000 -auth-service-url = https://kbase.us/services/authorization -globus-url = https://nexus.api.globusonline.org +auth-service-url = https://ci.kbase.us/services/auth/api/legacy/KBase/Sessions/Login diff --git a/deployment/bin/start-server.sh b/deployment/bin/start-server.sh new file mode 100755 index 0000000..ca47f43 --- /dev/null +++ b/deployment/bin/start-server.sh @@ -0,0 +1,4 @@ +# this is a dummy file set with correct permissions so that when it's overwritten by +# Dockerize the script can run. + +# TODO get rid of all this dockerize stuff and just use env vars for everything, like collections \ No newline at end of file diff --git a/deployment/conf/.templates/deployment.cfg.templ b/deployment/conf/.templates/deployment.cfg.templ index 8eaf18e..b30ba1f 100644 --- a/deployment/conf/.templates/deployment.cfg.templ +++ b/deployment/conf/.templates/deployment.cfg.templ @@ -17,20 +17,15 @@ mongodb-host = {{ default .Env.mongodb_host "ci-mongo" }} # name of the workspace mongo database mongodb-database = {{ default .Env.mongodb_database "user_profile_db" }} # the user name for an account with readWrite access to the database -mongodb-user = {{ default .Env.mongodb_user "upserv" }} +mongodb-user = {{ default .Env.mongodb_user "" }} # password for the account -mongodb-pwd = {{ default .Env.mongodb_pwd "fake" }} +mongodb-pwd = {{ default .Env.mongodb_pwd "" }} -# MongoDB reconnect retry count. The service will try to reconnect 1/s until -# this limit has been reached. -mongodb-retry = 0 +# whether to enable the MongoDB retryWrites parameter or not +mongodb-retrywrites={{ default .Env.mongodb_retrywrites "false" }} # the user name for an administrator admin = {{ default .Env.admin "kbuserprofileadmin" }} -# Base directory -basedir={{ default .Env.basedir "user_profile" }} - auth-service-url = {{ default .Env.auth_service_url "https://ci-kbase.us/services/authorization" }} auth-service-url-allow-insecure = {{ default .Env.auth_service_allow_insecure "false" }} -globus-url = {{ default .Env.globus_url "https://nexus.api.globusonline.org" }} diff --git a/deployment/conf/example.ini b/deployment/conf/example.ini index 9780d8d..00288d4 100644 --- a/deployment/conf/example.ini +++ b/deployment/conf/example.ini @@ -6,10 +6,8 @@ mongodb_database=user_profile_db mongodb_user=upserv mongodb_pwd=fake admin=kbuserprofileadmin -basedir=user_profile -auth_service_url=https://ci-kbase.us/services/authorization +auth_service_url=https://ci.kbase.us/services/auth/api/legacy/KBase/Sessions/Login auth_service_allow_insecure=false -globus_url=https://nexus.api.globusonline.org port=8080 server_threads=20 max_memory=3000 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f5cfeac --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,40 @@ +version: '3.4' + +services: + user_profile: + # build the nms docker image locally + build: . + # to use the latest docker image from ghcr.io + # uncomment the next line and comment out the build line + # image: ghcr.io/kbase/user_profile:latest + ports: + - "8080:8080" + depends_on: ["mongo"] + environment: + # see deployment/conf/.templates for more environment variables + - mongodb_host=mongo:27017 + - mongodb_database=user_profile + - auth_service_url=https://ci.kbase.us/services/auth/api/legacy/KBase/Sessions/Login + - auth_service_url_allow_insecure=false + - port=8080 + command: + - "-wait" + - "tcp://mongo:27017" + - "-timeout" + - "120s" + - "-template" + - "/kb/deployment/conf/.templates/deployment.cfg.templ:/kb/deployment/conf/deployment.cfg" + - "-template" + - "/kb/deployment/conf/.templates/http.ini.templ:/kb/deployment/jettybase/start.d/http.ini" + - "-template" + - "/kb/deployment/conf/.templates/server.ini.templ:/kb/deployment/jettybase/start.d/server.ini" + - "-template" + - "/kb/deployment/conf/.templates/start-server.sh.templ:/kb/deployment/bin/start-server.sh" + - "-stdout" + - "/kb/deployment/jettybase/logs/request.log" + - "/kb/deployment/bin/start-server.sh" + + mongo: + image: "mongo:3.6.23" + ports: + - "27017:27017" diff --git a/docshtml/KIDLspec.css b/docshtml/KIDLspec.css new file mode 100644 index 0000000..4d2a3e3 --- /dev/null +++ b/docshtml/KIDLspec.css @@ -0,0 +1,65 @@ +html, body { + height: 100%; +} +html { + display: table; + margin: auto; +} +body { + background-color: white; + color: #000; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + font-weight: normal; + font-size: 12px; + margin: 0; + padding: 20px; + display: table-cell; + vertical-align: middle; +} +span.space { + display: inline-block; + width: 7px; +} +span.tab { + display: inline-block; + width: 30px; +} +span.keyword { + font-weight: bold; + color: #008; +} +span.name { + color: #000; !important +} +span.deprecated { + text-decoration: line-through; +} +span.annotation { + color: #303030; +} +span.primitive { + font-weight: bold; + color: #066; +} +div.body { + background-color: #ffffff; + color: #3e4349; + padding: 0 30px; +} +div.comment { + color: #A0A0A0; +} +a { + color: #004b6b; + text-decoration: none; +} +a:hover { + color: #6d4100; + text-decoration: underline; +} +:target { + background-color: #ffa; +} +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} diff --git a/docshtml/UserProfile.html b/docshtml/UserProfile.html new file mode 100644 index 0000000..f4aa621 --- /dev/null +++ b/docshtml/UserProfile.html @@ -0,0 +1 @@ +UserProfile
moduleUserProfile{

/*
*@range[0,1]
*/
typedefintbool;

typedefstringusername;

typedefstringrealname;

typedefstructure{
usernameusername;
realnamerealname;
stringthumbnail;
}
User;

typedefstructure{
Useruser;
UnspecifiedObjectprofile;
}
UserProfile;

funcdefver()returns(string)authenticationnone;

typedefstructure{
stringfilter;
}
FilterParams;

/*
*Returns a list of users matching the filter. If the 'filter' field
*is empty or null, then this will return all Users. The filter will
*match substrings in usernames and realnames.
*/
funcdeffilter_users(FilterParamsp)returns(list<User>users)authenticationnone;

/*
*Given a list of usernames, returns a list of UserProfiles in the same order.
*If no UserProfile was found for a username, the UserProfile at that position will
*be null.
*/
funcdefget_user_profile(list<username>usernames)returns(list<UserProfile>profiles)authenticationnone;

typedefstructure{
UserProfileprofile;
}
SetUserProfileParams;

/*
*Set the UserProfile for the user indicated in the User field of the UserProfile
*object. This operation can only be performed if authenticated as the user in
*the UserProfile or as the admin account of this service.
*
*If the profile does not exist, one will be created. If it does already exist,
*then the entire user profile will be replaced with the new profile.
*/
funcdefset_user_profile(SetUserProfileParamsp)returns()authenticationrequired;

/*
*Update the UserProfile for the user indicated in the User field of the UserProfile
*object. This operation can only be performed if authenticated as the user in
*the UserProfile or as the admin account of this service.
*
*If the profile does not exist, one will be created. If it does already exist,
*then the specified top-level fields in profile will be updated.
*
*todo: add some way to remove fields. Fields in profile can only be modified or added.
*/
funcdefupdate_user_profile(SetUserProfileParamsp)returns()authenticationrequired;
};
\ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..4ac3234 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,2 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e644113 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..b82aa23 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..1aa94a4 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/hooks/build b/hooks/build deleted file mode 100755 index ffed924..0000000 --- a/hooks/build +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# See the docs for automated docker builds: https://docs.docker.com/docker-cloud/builds/advanced/ - -# $IMAGE_NAME var is injected into the build so the tag is correct. - -echo "Build hook running" -export BRANCH=${TRAVIS_BRANCH:-`git symbolic-ref --short HEAD`} -export DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` -export COMMIT=${TRAVIS_COMMIT:-`git rev-parse --short HEAD`} -docker build --build-arg BUILD_DATE=$DATE \ - --build-arg VCS_REF=$COMMIT \ - --build-arg BRANCH=$BRANCH \ - -t ${IMAGE_NAME} . diff --git a/jettybase/webapps/ROOT.war b/jettybase/webapps/ROOT.war deleted file mode 100644 index dcbebb6..0000000 Binary files a/jettybase/webapps/ROOT.war and /dev/null differ diff --git a/lib/Bio/KBase/UserProfile/Client.pm b/lib/Bio/KBase/UserProfile/Client.pm index 50571f1..7940473 100644 --- a/lib/Bio/KBase/UserProfile/Client.pm +++ b/lib/Bio/KBase/UserProfile/Client.pm @@ -37,7 +37,7 @@ sub new if (!defined($url)) { - $url = 'https://kbase.us/services/user_profile/rpc'; + $url = 'https://ci.kbase.us/services/user_profile/rpc'; } my $self = { @@ -85,12 +85,19 @@ sub new # We create an auth token, passing through the arguments that we were (hopefully) given. { - my $token = Bio::KBase::AuthToken->new(@args); + my %arg_hash2 = @args; + if (exists $arg_hash2{"token"}) { + $self->{token} = $arg_hash2{"token"}; + } elsif (exists $arg_hash2{"user_id"}) { + my $token = Bio::KBase::AuthToken->new(@args); + if (!$token->error_message) { + $self->{token} = $token->token; + } + } - if (!$token->error_message) + if (exists $self->{token}) { - $self->{token} = $token->token; - $self->{client}->{token} = $token->token; + $self->{client}->{token} = $self->{token}; } } @@ -590,98 +597,6 @@ todo: add some way to remove fields. Fields in profile can only be modified or } } - - -=head2 lookup_globus_user - - $users = $obj->lookup_globus_user($usernames) - -=over 4 - -=item Parameter and return types - -=begin html - -
-$usernames is a reference to a list where each element is an UserProfile.username
-$users is a reference to a hash where the key is an UserProfile.username and the value is an UserProfile.GlobusUser
-username is a string
-GlobusUser is a reference to a hash where the following keys are defined:
-	email has a value which is a string
-	fullName has a value which is a string
-	userName has a value which is a string
-
-
- -=end html - -=begin text - -$usernames is a reference to a list where each element is an UserProfile.username -$users is a reference to a hash where the key is an UserProfile.username and the value is an UserProfile.GlobusUser -username is a string -GlobusUser is a reference to a hash where the following keys are defined: - email has a value which is a string - fullName has a value which is a string - userName has a value which is a string - - -=end text - -=item Description - - - -=back - -=cut - - sub lookup_globus_user -{ - my($self, @args) = @_; - -# Authentication: required - - if ((my $n = @args) != 1) - { - Bio::KBase::Exceptions::ArgumentValidationError->throw(error => - "Invalid argument count for function lookup_globus_user (received $n, expecting 1)"); - } - { - my($usernames) = @args; - - my @_bad_arguments; - (ref($usernames) eq 'ARRAY') or push(@_bad_arguments, "Invalid type for argument 1 \"usernames\" (value was \"$usernames\")"); - if (@_bad_arguments) { - my $msg = "Invalid arguments passed to lookup_globus_user:\n" . join("", map { "\t$_\n" } @_bad_arguments); - Bio::KBase::Exceptions::ArgumentValidationError->throw(error => $msg, - method_name => 'lookup_globus_user'); - } - } - - my $url = $self->{url}; - my $result = $self->{client}->call($url, $self->{headers}, { - method => "UserProfile.lookup_globus_user", - params => \@args, - }); - if ($result) { - if ($result->is_error) { - Bio::KBase::Exceptions::JSONRPC->throw(error => $result->error_message, - code => $result->content->{error}->{code}, - method_name => 'lookup_globus_user', - data => $result->content->{error}->{error} # JSON::RPC::ReturnObject only supports JSONRPC 1.1 or 1.O - ); - } else { - return wantarray ? @{$result->result} : $result->result->[0]; - } - } else { - Bio::KBase::Exceptions::HTTP->throw(error => "Error invoking method lookup_globus_user", - status_line => $self->{client}->status_line, - method_name => 'lookup_globus_user', - ); - } -} - sub status { @@ -725,16 +640,16 @@ sub version { Bio::KBase::Exceptions::JSONRPC->throw( error => $result->error_message, code => $result->content->{code}, - method_name => 'lookup_globus_user', + method_name => 'update_user_profile', ); } else { return wantarray ? @{$result->result} : $result->result->[0]; } } else { Bio::KBase::Exceptions::HTTP->throw( - error => "Error invoking method lookup_globus_user", + error => "Error invoking method update_user_profile", status_line => $self->{client}->status_line, - method_name => 'lookup_globus_user', + method_name => 'update_user_profile', ); } } @@ -980,40 +895,6 @@ profile has a value which is an UserProfile.UserProfile -=head2 GlobusUser - -=over 4 - - - -=item Definition - -=begin html - -
-a reference to a hash where the following keys are defined:
-email has a value which is a string
-fullName has a value which is a string
-userName has a value which is a string
-
-
- -=end html - -=begin text - -a reference to a hash where the following keys are defined: -email has a value which is a string -fullName has a value which is a string -userName has a value which is a string - - -=end text - -=back - - - =cut package Bio::KBase::UserProfile::Client::RpcClient; diff --git a/lib/biokbase/user_profile/authclient.py b/lib/biokbase/user_profile/authclient.py deleted file mode 100644 index 9a15713..0000000 --- a/lib/biokbase/user_profile/authclient.py +++ /dev/null @@ -1,91 +0,0 @@ -''' -Created on Aug 1, 2016 - -A very basic KBase auth client for the Python server. - -@author: gaprice@lbl.gov -''' -import time as _time -import requests as _requests -import threading as _threading -import hashlib - - -class TokenCache(object): - ''' A basic cache for tokens. ''' - - _MAX_TIME_SEC = 5 * 60 # 5 min - - _lock = _threading.RLock() - - def __init__(self, maxsize=2000): - self._cache = {} - self._maxsize = maxsize - self._halfmax = maxsize / 2 # int division to round down - - def get_user(self, token): - token = hashlib.sha256(token).hexdigest() - with self._lock: - usertime = self._cache.get(token) - if not usertime: - return None - - user, intime = usertime - if _time.time() - intime > self._MAX_TIME_SEC: - return None - return user - - def add_valid_token(self, token, user): - if not token: - raise ValueError('Must supply token') - if not user: - raise ValueError('Must supply user') - token = hashlib.sha256(token).hexdigest() - with self._lock: - self._cache[token] = [user, _time.time()] - if len(self._cache) > self._maxsize: - for i, (t, _) in enumerate(sorted(self._cache.items(), - key=lambda (_, v): v[1])): - if i <= self._halfmax: - del self._cache[t] - else: - break - - -class KBaseAuth(object): - ''' - A very basic KBase auth client for the Python server. - ''' - - _LOGIN_URL = 'https://kbase.us/services/authorization/Sessions/Login' - - def __init__(self, auth_url=None): - ''' - Constructor - ''' - self._authurl = auth_url - if not self._authurl: - self._authurl = self._LOGIN_URL - self._cache = TokenCache() - - def get_user(self, token): - if not token: - raise ValueError('Must supply token') - user = self._cache.get_user(token) - if user: - return user - - d = {'token': token, 'fields': 'user_id'} - ret = _requests.post(self._authurl, data=d) - if not ret.ok: - try: - err = ret.json() - except: - ret.raise_for_status() - raise ValueError('Error connecting to auth service: {} {}\n{}' - .format(ret.status_code, ret.reason, - err['error_msg'])) - - user = ret.json()['user_id'] - self._cache.add_valid_token(token, user) - return user diff --git a/lib/biokbase/user_profile/baseclient.py b/lib/biokbase/user_profile/baseclient.py index 3d2a61a..7dc1ce1 100644 --- a/lib/biokbase/user_profile/baseclient.py +++ b/lib/biokbase/user_profile/baseclient.py @@ -11,6 +11,9 @@ import requests as _requests import random as _random import os as _os +import traceback as _traceback +from requests.exceptions import ConnectionError +from urllib3.exceptions import ProtocolError try: from configparser import ConfigParser as _ConfigParser # py 3 @@ -26,6 +29,7 @@ _CT = 'content-type' _AJ = 'application/json' _URL_SCHEME = frozenset(['http', 'https']) +_CHECK_JOB_RETRYS = 3 def _get_token(user_id, password, auth_svc): @@ -121,7 +125,7 @@ def __init__( self, url=None, timeout=30 * 60, user_id=None, password=None, token=None, ignore_authrc=False, trust_all_ssl_certificates=False, - auth_svc='https://kbase.us/services/authorization/Sessions/Login', + auth_svc='https://kbase.us/services/auth/api/legacy/KBase/Sessions/Login', lookup_url=False, async_job_check_time_ms=100, async_job_check_time_scale_percent=150, @@ -236,20 +240,30 @@ def run_job(self, service_method, args, service_ver=None, context=None): mod, _ = service_method.split('.') job_id = self._submit_job(service_method, args, service_ver, context) async_job_check_time = self.async_job_check_time - while True: + check_job_failures = 0 + while check_job_failures < _CHECK_JOB_RETRYS: time.sleep(async_job_check_time) async_job_check_time = (async_job_check_time * self.async_job_check_time_scale_percent / 100.0) if async_job_check_time > self.async_job_check_max_time: async_job_check_time = self.async_job_check_max_time - job_state = self._check_job(mod, job_id) + + try: + job_state = self._check_job(mod, job_id) + except (ConnectionError, ProtocolError): + _traceback.print_exc() + check_job_failures += 1 + continue + if job_state['finished']: if not job_state['result']: return if len(job_state['result']) == 1: return job_state['result'][0] return job_state['result'] + raise RuntimeError("_check_job failed {} times and exceeded limit".format( + check_job_failures)) def call_method(self, service_method, args, service_ver=None, context=None): diff --git a/lib/biokbase/user_profile/client.py b/lib/biokbase/user_profile/client.py index ae8244f..60ff366 100644 --- a/lib/biokbase/user_profile/client.py +++ b/lib/biokbase/user_profile/client.py @@ -12,7 +12,7 @@ try: # baseclient and this client are in a package from .baseclient import BaseClient as _BaseClient # @UnusedImport -except: +except ImportError: # no they aren't from baseclient import BaseClient as _BaseClient # @Reimport @@ -23,9 +23,9 @@ def __init__( self, url=None, timeout=30 * 60, user_id=None, password=None, token=None, ignore_authrc=False, trust_all_ssl_certificates=False, - auth_svc='https://kbase.us/services/authorization/Sessions/Login'): + auth_svc='https://ci.kbase.us/services/auth/api/legacy/KBase/Sessions/Login'): if url is None: - url = 'https://kbase.us/services/user_profile/rpc' + url = 'https://ci.kbase.us/services/user_profile/rpc' self._service_ver = None self._client = _BaseClient( url, timeout=timeout, user_id=user_id, password=password, @@ -37,9 +37,8 @@ def ver(self, context=None): """ :returns: instance of String """ - return self._client.call_method( - 'UserProfile.ver', - [], self._service_ver, context) + return self._client.call_method('UserProfile.ver', + [], self._service_ver, context) def filter_users(self, p, context=None): """ @@ -52,9 +51,8 @@ def filter_users(self, p, context=None): "username" of type "username", parameter "realname" of type "realname", parameter "thumbnail" of String """ - return self._client.call_method( - 'UserProfile.filter_users', - [p], self._service_ver, context) + return self._client.call_method('UserProfile.filter_users', + [p], self._service_ver, context) def get_user_profile(self, usernames, context=None): """ @@ -68,9 +66,8 @@ def get_user_profile(self, usernames, context=None): parameter "thumbnail" of String, parameter "profile" of unspecified object """ - return self._client.call_method( - 'UserProfile.get_user_profile', - [usernames], self._service_ver, context) + return self._client.call_method('UserProfile.get_user_profile', + [usernames], self._service_ver, context) def set_user_profile(self, p, context=None): """ @@ -85,9 +82,8 @@ def set_user_profile(self, p, context=None): "username", parameter "realname" of type "realname", parameter "thumbnail" of String, parameter "profile" of unspecified object """ - return self._client.call_method( - 'UserProfile.set_user_profile', - [p], self._service_ver, context) + return self._client.call_method('UserProfile.set_user_profile', + [p], self._service_ver, context) def update_user_profile(self, p, context=None): """ @@ -103,20 +99,8 @@ def update_user_profile(self, p, context=None): "username", parameter "realname" of type "realname", parameter "thumbnail" of String, parameter "profile" of unspecified object """ - return self._client.call_method( - 'UserProfile.update_user_profile', - [p], self._service_ver, context) - - def lookup_globus_user(self, usernames, context=None): - """ - :param usernames: instance of list of type "username" - :returns: instance of mapping from type "username" to type - "GlobusUser" -> structure: parameter "email" of String, parameter - "fullName" of String, parameter "userName" of String - """ - return self._client.call_method( - 'UserProfile.lookup_globus_user', - [usernames], self._service_ver, context) + return self._client.call_method('UserProfile.update_user_profile', + [p], self._service_ver, context) def status(self, context=None): return self._client.call_method('UserProfile.status', diff --git a/lib/javascript/UserProfile/Client.js b/lib/javascript/UserProfile/Client.js index df60872..0417b33 100644 --- a/lib/javascript/UserProfile/Client.js +++ b/lib/javascript/UserProfile/Client.js @@ -17,7 +17,7 @@ function UserProfile(url, auth, auth_cb, timeout, async_job_check_time_ms, servi this.service_version = service_version; if (typeof(_url) != "string" || _url.length == 0) { - _url = "https://kbase.us/services/user_profile/rpc"; + _url = "https://ci.kbase.us/services/user_profile/rpc"; } var _auth = auth ? auth : { 'token' : '', 'user_id' : ''}; var _auth_cb = auth_cb; @@ -84,19 +84,6 @@ function UserProfile(url, auth, auth_cb, timeout, async_job_check_time_ms, servi return json_call_ajax(_url, "UserProfile.update_user_profile", [p], 0, _callback, _errorCallback); }; - - this.lookup_globus_user = function (usernames, _callback, _errorCallback) { - if (typeof usernames === 'function') - throw 'Argument usernames can not be a function'; - if (_callback && typeof _callback !== 'function') - throw 'Argument _callback must be a function if defined'; - if (_errorCallback && typeof _errorCallback !== 'function') - throw 'Argument _errorCallback must be a function if defined'; - if (typeof arguments === 'function' && arguments.length > 1+2) - throw 'Too many arguments ('+arguments.length+' instead of '+(1+2)+')'; - return json_call_ajax(_url, "UserProfile.lookup_globus_user", - [usernames], 1, _callback, _errorCallback); - }; this.status = function (_callback, _errorCallback) { if (_callback && typeof _callback !== 'function') diff --git a/server_scripts/build_server_control_scripts.py b/server_scripts/build_server_control_scripts.py deleted file mode 100755 index 6175ce8..0000000 --- a/server_scripts/build_server_control_scripts.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python -''' -Created on Mar 11, 2014 - -@author: gaprice@lbl.gov -''' -from __future__ import print_function -import sys -from configobj import ConfigObj -import os -import stat - -PORT = 'port' -THREADS = 'server-threads' -MINMEM = 'min-memory' -MAXMEM = 'max-memory' - - -def printerr(*objs): - print(*objs, file=sys.stderr) - sys.exit(1) - - -def make_executable(path): - st = os.stat(path) - os.chmod(path, st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) - - -def getConfig(param, cfg, cfile): - if param not in cfg: - printerr('Missing expected parameter {} in config file {}' - .format(param, cfile)) - return cfg[param] - -if len(sys.argv) < 8: - printerr("Missing arguments to build_server_control_scripts") -if len(sys.argv) == 8: - _, serviceDir, war, target, javaHome, deployCfg, asadmin, serviceDomain =\ - sys.argv - port = None -else: - _, serviceDir, war, target, javaHome, deployCfg, asadmin, serviceDomain,\ - port = sys.argv - -if not os.path.isfile(deployCfg): - printerr('Configuration parameter is not a file: ' + deployCfg) -cfg = ConfigObj(deployCfg) -if serviceDomain not in cfg: - printerr('No {} section in config file {} - '.format( - serviceDomain, deployCfg)) -wscfg = cfg[serviceDomain] - -if port is None: - if PORT not in wscfg: - printerr("Port not provided as argument or in config") - port = wscfg[PORT] - -threads = getConfig(THREADS, wscfg, deployCfg) -minmem = getConfig(MINMEM, wscfg, deployCfg) -maxmem = getConfig(MAXMEM, wscfg, deployCfg) - -with open(os.path.join(serviceDir, 'start_service'), 'w') as ss: - ss.write('export JAVA_HOME={}\n'.format(javaHome)) - ss.write('export PATH=$JAVA_HOME/bin:$PATH\n') - ss.write(('JARS={}/lib/jars\n').format(target)) - ss.write('if [ -z "$KB_DEPLOYMENT_CONFIG" ]\n') - ss.write('then\n') - ss.write(' export KB_DEPLOYMENT_CONFIG={}/deployment.cfg\n' - .format(target)) - ss.write('fi\n') - ss.write(('cd {}\n').format(serviceDir)) - ss.write(('java -cp $JARS/jetty/jetty-start-7.0.0.jar:$JARS/jetty/jetty-all-7.0.0.jar:$JARS/servlet/servlet-api-2.5.jar ' + - '-Xmx{}m ' + - '-Djetty.port={} ' + - '-DKB_DEPLOYMENT_CONFIG=$KB_DEPLOYMENT_CONFIG ' + - 'org.eclipse.jetty.start.Main jetty.xml\n') - .format(maxmem,port,serviceDir,serviceDir)) - -with open(os.path.join(serviceDir, 'stop_service'), 'w') as ss: - ss.write('export JAVA_HOME={}\n'.format(javaHome)) - ss.write('export PATH=$JAVA_HOME/bin:$PATH\n') - ss.write('export CLASSPATH=\n') - ss.write('killall java\n') - -make_executable(os.path.join(serviceDir, 'start_service')) -make_executable(os.path.join(serviceDir, 'stop_service')) diff --git a/server_scripts/get_deploy_cfg.pm b/server_scripts/get_deploy_cfg.pm deleted file mode 100644 index bcded36..0000000 --- a/server_scripts/get_deploy_cfg.pm +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; -use Getopt::Long; -use Config::Simple; - -my $DESCRIPTION = -" -usage: - get_deploy_cfg [Options] [VariableName] - - Retrieve the value of a variable in a KBase 'deploy.cfg' file (essentially a simple - INI file) by variable name. The value is printed to standard out. - - -d, --deploy-cfg [FILE] - (optional) the location of the deploy.cfg file to use; if not provided, the - script assumes the file is named 'deploy.cfg' in your current working directory - - -h, --help - diplay this help message, ignore all arguments -"; - -my $help = ''; -my $deploy_cfg_file = 'deploy.cfg'; -my $opt = GetOptions ( - "help|h" => \$help, - "deploy-cfg|d=s" => \$deploy_cfg_file - ); -if($help) { - print $DESCRIPTION; - exit 0; -} - -#process args -my $n_args = $#ARGV+1; -if ($n_args != 1) { - print STDERR "ERROR: Incorrect number of arguments- must specify a variable name.\n"; - print STDERR $DESCRIPTION; - exit 1; -} -my $varname = $ARGV[0]; - -#read the cfg file -if (!-e $deploy_cfg_file) { - print STDERR "ERROR: Cannot find deploy.cfg (looking for '$deploy_cfg_file').\n"; - print STDERR "Rerun with --help option for usage.\n"; - exit 1; -} -my $cfg_lookup={}; -Config::Simple->import_from($deploy_cfg_file, $cfg_lookup); - -if (defined $cfg_lookup->{$varname}) { - print STDOUT $cfg_lookup->{$varname}; -} else { - print STDERR "ERROR: Variable '$varname' not defined in config file '$deploy_cfg_file').\n"; - print STDERR " Available variables are: \n"; - if (scalar(keys(%$cfg_lookup))==0) { - print STDERR " --no variables were found--\n"; - } - else { - foreach my $key (keys %$cfg_lookup) { - print STDERR " $key\n"; - } - } -} - -exit 0; - - - - diff --git a/server_scripts/glassfish_administer_service.py b/server_scripts/glassfish_administer_service.py deleted file mode 100755 index f07a370..0000000 --- a/server_scripts/glassfish_administer_service.py +++ /dev/null @@ -1,315 +0,0 @@ -#!/usr/bin/env python -''' -Created on Dec 6, 2013 - -@author: gaprice@lbl.gov -''' -from __future__ import print_function -from argparse import ArgumentParser -import subprocess -import os -import xml.etree.ElementTree as ET -import urllib2 -from subprocess import CalledProcessError -import sys - -_PARALLEL_GC = "-XX:-UseParallelGC" -_PARALLEL_GC_ESC = "-XX\:-UseParallelGC" - - -def _parseArgs(): - parser = ArgumentParser(description='script to administer a Glassfish ' + - ' application.') - parser.add_argument('-w', '--war', - help='path to the application WAR file. If ' + - 'omitted, the service at the port and domain is ' + - 'stopped.') - parser.add_argument('-a', '--admin', required=True, - help='location of the Glassfish asadmin program.') - parser.add_argument('-d', '--domain', required=True, - help='name of the Glassfish domain where the ' + - 'application is or will be installed.') - parser.add_argument('-l', '--domain-dir', - help='directory where the glassfish domain ' + - 'information and logs will be stored. Defaults to ' + - 'glassfish/domains.') - parser.add_argument('-p', '--port', required=True, type=int, - help='the port where the application runs.') - parser.add_argument('-t', '--threads', type=int, default=20, - help='the number of threads for the application.') - parser.add_argument('-s', '--Xms', type=int, - help='minimum memory for the domain in MB. ' + - 'This will cause a domain restart if changed.') - parser.add_argument('-x', '--Xmx', type=int, - help='maximum memory for the domain in MB. ' + - 'This will cause a domain restart if changed.') - parser.add_argument('-r', '--properties', nargs='*', - help='JVM system properties to add to the server.') - parser.add_argument('-g', '--noparallelgc', action='store_true', - help='turn off the parallel garbage ' + - ' collector and use the standard gc.') - return parser.parse_args() - - -class CommandGlassfishDomain(object): - - def __init__(self, asadminpath, domain, domainpath): - self.asadminpath = asadminpath - self.domain = domain - self.path = None - if (domainpath): - domaindir = os.path.abspath(os.path.expanduser(domainpath)) - if not os.path.isdir(domaindir): - if not os.path.exists(domaindir): - os.mkdir(domaindir) - else: - print('Domain path ' + domainpath + ' must be a directory') - sys.exit(1) - self.path = domaindir - p = (' at ' + self.path) if(self.path) else '' - if self.exists(): - print('Domain ' + self.domain + ' exists' + p + - ', skipping creation') - else: - print('Creating domain ' + self.domain + p) - print(self._run_local_command('create-domain', '--nopassword=true', - self.domain).rstrip()) - self.adminport = self.get_admin_port() - self.start_domain() - - def get_admin_port(self): - #the fact I have to do this is moronic - if (self.path): - domains = self.path - else: - bindir = os.path.dirname(self.asadminpath) - glassfish = os.path.join(bindir, "..") - domains = os.path.join(glassfish, "domains") - domain = os.path.join(domains, self.domain) - configfile = os.path.join(domain, "config/domain.xml") - xml = ET.parse(configfile) - root = xml.getroot() - config = root.findall("./configs/config[@name='server-config']")[0] - adminlist = config.findall( - "./network-config/network-listeners/network-listener[@protocol=" + - "'admin-listener']")[0] - return adminlist.attrib['port'] - - def start_domain(self): - if self.is_running(): - print ("Domain " + self.domain + " is already running on port " + - self.adminport) - else: - print("Starting domain " + self.domain) - print(self._run_local_command('start-domain', self.domain) - .rstrip()) - self.adminport = self.get_admin_port() - - def restart_domain(self): - if self.is_running(): - print("Restarting " + self.domain + ", please wait") - print(self._run_local_command('restart-domain', self.domain) - .rstrip()) - else: - self.start_domain() - - def exists(self): - return self.domain in self._list_domains() - - def is_running(self): - return self.domain + " running" in self._list_domains() - - def start_service(self, war, port, threads): - portstr = str(port) - threadstr = str(threads) - if 'server-' + portstr in self._run_remote_command( - 'list-virtual-servers'): - print("Virtual server already exists") - else: - print(self._run_remote_command( - 'create-virtual-server', '--hosts', - '${com.sun.aas.hostName}', 'server-' + portstr).rstrip()) - if 'thread-pool-' + portstr in self._run_remote_command( - 'list-threadpools', 'server'): - print("Threadpool already exists") - else: - print(self._run_remote_command( - 'create-threadpool', '--maxthreadpoolsize=' + threadstr, - '--minthreadpoolsize=' + threadstr, 'thread-pool-' + portstr) - .rstrip()) - if 'http-listener-' + portstr in self._run_remote_command( - 'list-http-listeners'): - print('Http listener already exists') - else: - print(self._run_remote_command( - 'create-http-listener', '--listeneraddress', '0.0.0.0', - '--listenerport', portstr, - '--default-virtual-server', 'server-' + portstr, - '--securityEnabled=false', '--acceptorthreads=' + threadstr, - 'http-listener-' + portstr).rstrip()) - print(self._run_remote_command( - 'set', 'server.network-config.network-listeners.' + - 'network-listener.http-listener-' + portstr + - '.thread-pool=thread-pool-' + portstr).rstrip()) - print(self._run_remote_command( - 'set', 'server.network-config.protocols.protocol.' + - 'http-listener-' + portstr + '.http.timeout-seconds=1800') - .rstrip()) - if 'app-' + portstr in self._run_remote_command('list-applications'): - print(self._run_remote_command('undeploy', 'app-' + portstr) - .rstrip()) - print(self._run_remote_command( - 'deploy', '--virtualservers', 'server-' + portstr, - '--contextroot', '/', '--name', 'app-' + portstr, war).rstrip()) - try: - localCheckUrl = 'http://localhost:' + portstr + '/rpc' - print('Attempting to check if rpc service is up here: '+localCheckUrl) - urllib2.urlopen(localCheckUrl) - except urllib2.HTTPError as h: - resp = h.read() - else: - print('Unexpected response from server - the server did not ' + - 'start up successfully. Please check the glassfish logs.') - return False - if '32603' in resp: - print('The server failed to start up successfully and is ' + - 'running in protected mode. Please check the system and ' + - 'glassfish logs.') - return False - elif '32300' in resp: - print('The server started successfully.') - return True - else: - print('The server failed to start up successfully and is not ' - + 'running. Please check the system and glassfish logs.') - return False - - def stop_service(self, port): - portstr = str(port) - if 'app-' + portstr in self._run_remote_command('list-applications'): - print(self._run_remote_command('undeploy', 'app-' + portstr) - .rstrip()) - if 'http-listener-' + portstr in self._run_remote_command( - 'list-http-listeners'): - print(self._run_remote_command( - 'delete-http-listener', 'http-listener-' + portstr).rstrip()) - if 'http-listener-' + portstr in self._run_remote_command( - 'list-protocols'): - print(self._run_remote_command( - 'delete-protocol', 'http-listener-' + portstr).rstrip()) - if 'thread-pool-' + portstr in self._run_remote_command( - 'list-threadpools', 'server'): - print(self._run_remote_command( - 'delete-threadpool', 'thread-pool-' + portstr).rstrip()) - if 'server-' + portstr in self._run_remote_command( - 'list-virtual-servers'): - print(self._run_remote_command( - 'delete-virtual-server', 'server-' + portstr).rstrip()) - - def set_min_max_memory(self, minm, maxm): - # will restart the domain if changes are necessary - xmx = [] - xms = [] - for o in self._run_remote_command('list-jvm-options').split('\n'): - if o.startswith('-Xmx'): - xmx.append(o) - if o.startswith('-Xms'): - xms.append(o) - if (len(xms) > 1 and minm is None): - print('WARNING: multiple Xms parameters set on service: ' + - str(xms)) - if (len(xmx) > 1 and maxm is None): - print('WARNING: multiple Xmx parameters set on service: ' + - str(xmx)) - changed = self._set_memory(None if minm is None else '-Xms' + - str(minm) + 'm', xms) - changed2 = self._set_memory(None if maxm is None else '-Xmx' - + str(maxm) + 'm', xmx) - if changed or changed2: - self.restart_domain() - - def reenable_parallel_gc(self): - if self.parallel_gc_is_disabled(): - self.delete_jvm_option(_PARALLEL_GC_ESC) - self.restart_domain() - - def parallel_gc_is_disabled(self): - for o in self._run_remote_command('list-jvm-options').split('\n'): - if o == _PARALLEL_GC: - return True - return False - - def stop_parallel_gc(self): - if not self.parallel_gc_is_disabled(): - self.create_jvm_option(_PARALLEL_GC_ESC) - self.restart_domain() - - def create_property(self, prop): - print('Creating property ' + prop) - print(self._run_remote_command('create-system-properties', prop) - .rstrip()) - - def create_jvm_option(self, prop): - print('Creating jvm property ' + prop) - print(self._run_remote_command('create-jvm-options', prop) - .rstrip()) - - def delete_jvm_option(self, prop): - print('Removing jvm property ' + prop) - print(self._run_remote_command('delete-jvm-options', prop).rstrip()) - - def _set_memory(self, memstr, memlist): - if (memstr is not None and [memstr] != memlist): - print("Removing options " + str(memlist)) - for o in memlist: - self._remove_option(o) - print("Setting option " + memstr) - self._set_option(memstr) - return True - else: - return False - - def _set_option(self, opt): - self._run_remote_command('create-jvm-options', opt) - - def _remove_option(self, opt): - self._run_remote_command('delete-jvm-options', opt) - - def _list_domains(self): - return self._run_local_command('list-domains') - - def _run_local_command(self, subcmd, *args): - cmd = [self.asadminpath, subcmd] - if (self.path): - cmd.extend(['--domaindir', self.path]) - try: - return subprocess.check_output(cmd + list(args)) - except CalledProcessError as cpe: - print(cpe.output.rstrip()) - sys.exit(1) - - def _run_remote_command(self, *cmd): - try: - return subprocess.check_output([self.asadminpath, '-p', - self.adminport] + list(cmd)) - except CalledProcessError as cpe: - print(cpe.output.rstrip()) - sys.exit(1) - - -if __name__ == '__main__': - args = _parseArgs() - gf = CommandGlassfishDomain(args.admin, args.domain, args.domain_dir) - if (args.war == None): - gf.stop_service(args.port) - else: - if (args.noparallelgc): - gf.stop_parallel_gc() - else: - gf.reenable_parallel_gc() - gf.set_min_max_memory(args.Xms, args.Xmx) - for p in args.properties: - gf.create_property(p) - success = gf.start_service(args.war, args.port, args.threads) - if not success: - sys.exit(1) diff --git a/server_scripts/jetty.xml b/server_scripts/jetty.xml deleted file mode 100644 index ff58d23..0000000 --- a/server_scripts/jetty.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - 5 - 200 - - - - - - - 0.0.0.0 - - 1800000 - 2 - false - - 10000 - 5000 - - - - - - - - - - - - - - - - - - - - - - - - - /webapps/root.war - / - - - org.eclipse.jetty.webapp.WebInfConfiguration - org.eclipse.jetty.webapp.WebXmlConfiguration - org.eclipse.jetty.webapp.MetaInfConfiguration - org.eclipse.jetty.webapp.JettyWebXmlConfiguration - org.eclipse.jetty.webapp.TagLibConfiguration - - - /temp/ - true - true - false - - org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern - .*/.*jsp-api-[^/]*\.jar$|.*/.*jsp-[^/]*\.jar$|.*/.*taglibs[^/]*\.jar$ - - - - - - - ./yyyy_mm_dd.request.log - yyyy_MM_dd - 90 - true - false - false - GMT - - - - - true - true - true - 1000 - - diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..63e88cd --- /dev/null +++ b/settings.gradle @@ -0,0 +1,8 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.7/userguide/multi_project_builds.html in the Gradle documentation. + */ + +rootProject.name = 'user_profile' diff --git a/src/us/kbase/userprofile/FilterParams.java b/src/main/java/us/kbase/userprofile/FilterParams.java similarity index 100% rename from src/us/kbase/userprofile/FilterParams.java rename to src/main/java/us/kbase/userprofile/FilterParams.java diff --git a/src/main/java/us/kbase/userprofile/MongoController.java b/src/main/java/us/kbase/userprofile/MongoController.java new file mode 100644 index 0000000..40e38e4 --- /dev/null +++ b/src/main/java/us/kbase/userprofile/MongoController.java @@ -0,0 +1,278 @@ +package us.kbase.userprofile; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + + + +/* +import org.apache.commons.lang3.StringUtils;*/ + +import com.mongodb.client.FindIterable; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.IndexOptions; +import us.kbase.common.service.UObject; + +import com.fasterxml.jackson.databind.JsonNode; +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; +import org.bson.Document; + +public class MongoController { + + private static final String COL_PROFILES = "profiles"; + + private final MongoCollection profiles; + + public MongoController(final String host, final String database, final boolean mongoRetryWrites) { + final MongoDatabase db = getDB(host, database, null, null, mongoRetryWrites); + profiles = db.getCollection(COL_PROFILES); + ensureIndex(); + } + + public MongoController(final String host, final String database, + final String mongoUser, final String mongoPswd, + final boolean mongoRetryWrites) { + final MongoDatabase db = getDB(host, database, mongoUser, mongoPswd, mongoRetryWrites); + profiles = db.getCollection(COL_PROFILES); + ensureIndex(); + } + + private MongoDatabase getDB(final String host, final String db, final String user, + final String pwd, final boolean retryWrites) { + final MongoClientSettings.Builder mongoBuilder = MongoClientSettings.builder() + .retryWrites(retryWrites) + .applyToClusterSettings( + builder -> builder.hosts(Arrays.asList(new ServerAddress(host)))); + final MongoClient cli; + if (user != null) { + final MongoCredential creds = MongoCredential.createCredential( + user, db, pwd.toCharArray()); + // unclear if and when it's safe to clear the password + cli = MongoClients.create(mongoBuilder.credential(creds).build()); + } else { + cli = MongoClients.create(mongoBuilder.build()); + } + return cli.getDatabase(db); + } + + + public List filterUsers(String filter) { + List users = new ArrayList<>(); + + if(filter.trim().isEmpty()) { + //return all + FindIterable docs = profiles.find(new Document()).projection(new Document("user", 1)); + for (Document document : docs) { + Document d = document.get("user", Document.class); + // TODO we should check the DB to see if there are any users without usernames, and if not + // alter these lines so we assume the username always exists and throws an error otherwise + if (!d.containsKey("username")) { + continue; + } + User u = new User(); + u.setUsername(d.get("username").toString()); + + if (d.get("realname") != null) { + u.setRealname(d.get("realname").toString()); + } + if (d.get("thumbnail") != null) { + u.setThumbnail(d.get("thumbnail").toString()); + } + + users.add(u); + } + return users; + } + + // for now we do text search here instead of on the DB side + // todo: see if we can install a text search index + FindIterable docs = profiles.find(new Document()).projection(new Document("user", 1)); + String [] terms = filter.split("\\s+"); + for(Document document : docs) { + Document d = document.get("user", Document.class); + // TODO we should check the DB to see if there are any users without usernames, and if not + // alter these lines so we assume the username always exists and throws an error otherwise + if(!d.containsKey("username")) { + continue; + } + User u = new User(); + String uname = d.get("username").toString(); + u.setUsername(uname); + boolean add = true; + for(int i = 0; i < terms.length; i++) { + if(!uname.toLowerCase().contains(terms[i].toLowerCase())){ + add = false; break; + } + } + + if(d.containsKey("realname")) { + if(d.get("realname")!=null) { + String rname = d.get("realname").toString(); + u.setRealname(rname); + if(!add) { + add = true; + for(int i=0; i> fields = profileNode.fields(); + while(fields.hasNext()) { + final Map.Entry e = fields.next(); + final Object val = UObject.transformJacksonToObject(e.getValue(), Object.class); + update.put("profile." + e.getKey(), val); + } + } else { + throw new RuntimeException("Profile must be an object if defined."); + } + } + + profiles.updateOne( + new Document("user.username", up.getUser().getUsername()), + new Document("$set", update)); + + } else { + Document user = new Document("username", up.getUser().getUsername()) + .append("realname", up.getUser().getRealname()) + .append("thumbnail", up.getUser().getThumbnail()); + + Document profile = new Document("user", user); + if(up.getProfile() != null) { + if(up.getProfile().asJsonNode().isObject()) { + profile.put("profile", Document.parse(up.getProfile().asJsonNode().toString())); + } else { + throw new RuntimeException("Profile must be an object if defined."); + } + } else { + profile.put("profile", null); + } + profiles.insertOne(profile); + } + } + + +} diff --git a/src/us/kbase/userprofile/SetUserProfileParams.java b/src/main/java/us/kbase/userprofile/SetUserProfileParams.java similarity index 100% rename from src/us/kbase/userprofile/SetUserProfileParams.java rename to src/main/java/us/kbase/userprofile/SetUserProfileParams.java diff --git a/src/us/kbase/userprofile/User.java b/src/main/java/us/kbase/userprofile/User.java similarity index 100% rename from src/us/kbase/userprofile/User.java rename to src/main/java/us/kbase/userprofile/User.java diff --git a/src/us/kbase/userprofile/UserProfile.java b/src/main/java/us/kbase/userprofile/UserProfile.java similarity index 100% rename from src/us/kbase/userprofile/UserProfile.java rename to src/main/java/us/kbase/userprofile/UserProfile.java diff --git a/src/us/kbase/userprofile/UserProfileClient.java b/src/main/java/us/kbase/userprofile/UserProfileClient.java similarity index 92% rename from src/us/kbase/userprofile/UserProfileClient.java rename to src/main/java/us/kbase/userprofile/UserProfileClient.java index 7b26f7e..68a7727 100644 --- a/src/us/kbase/userprofile/UserProfileClient.java +++ b/src/main/java/us/kbase/userprofile/UserProfileClient.java @@ -25,7 +25,7 @@ public class UserProfileClient { private static URL DEFAULT_URL = null; static { try { - DEFAULT_URL = new URL("https://kbase.us/services/user_profile/rpc"); + DEFAULT_URL = new URL("https://ci.kbase.us/services/user_profile/rpc"); } catch (MalformedURLException mue) { throw new RuntimeException("Compile error in client - bad url compiled"); } @@ -292,23 +292,6 @@ public void updateUserProfile(SetUserProfileParams p, RpcContext... jsonRpcConte caller.jsonrpcCall("UserProfile.update_user_profile", args, retType, false, true, jsonRpcContext, this.serviceVersion); } - /** - *

Original spec-file function name: lookup_globus_user

- *
-     * 
- * @param usernames instance of list of original type "username" - * @return parameter "users" of mapping from original type "username" to type {@link us.kbase.userprofile.GlobusUser GlobusUser} - * @throws IOException if an IO exception occurs - * @throws JsonClientException if a JSON RPC exception occurs - */ - public Map lookupGlobusUser(List usernames, RpcContext... jsonRpcContext) throws IOException, JsonClientException { - List args = new ArrayList(); - args.add(usernames); - TypeReference>> retType = new TypeReference>>() {}; - List> res = caller.jsonrpcCall("UserProfile.lookup_globus_user", args, retType, true, true, jsonRpcContext, this.serviceVersion); - return res.get(0); - } - public Map status(RpcContext... jsonRpcContext) throws IOException, JsonClientException { List args = new ArrayList(); TypeReference>> retType = new TypeReference>>() {}; diff --git a/src/us/kbase/userprofile/UserProfileServer.java b/src/main/java/us/kbase/userprofile/UserProfileServer.java similarity index 69% rename from src/us/kbase/userprofile/UserProfileServer.java rename to src/main/java/us/kbase/userprofile/UserProfileServer.java index 132555c..6c55731 100644 --- a/src/us/kbase/userprofile/UserProfileServer.java +++ b/src/main/java/us/kbase/userprofile/UserProfileServer.java @@ -12,13 +12,6 @@ //BEGIN_HEADER import java.util.ArrayList; -import java.util.HashMap; - -import java.net.URL; - -import us.kbase.auth.ConfigurableAuthService; -import us.kbase.auth.AuthConfig; -import us.kbase.auth.UserDetail; import org.ini4j.Ini; //END_HEADER @@ -30,15 +23,12 @@ */ public class UserProfileServer extends JsonServerServlet { private static final long serialVersionUID = 1L; - private static final String version = "0.2.1"; - private static final String gitUrl = "https://github.com/kbase/user_profile"; - // the git commmit hash hasn't been updated in 2 years as of 3/1/19 and there doesn't appear - // to be a mechanism to update it correctly, as it needs to be SDK compiled from the - // current commit and then built into a server. Any checked in commit must be wrong. - private static final String gitCommitHash = ""; + private static final String version = "0.0.1"; + private static final String gitUrl = "https://github.com/kbase/user_profile.git"; + private static final String gitCommitHash = "8a56e04b45d01c27c45c65dd80f62c5b4dde885c"; //BEGIN_CLASS_HEADER - public static final String VERSION = "0.2.1"; + public static final String VERSION = "0.3.0"; public static final String SYS_PROP_KB_DEPLOYMENT_CONFIG = "KB_DEPLOYMENT_CONFIG"; public static final String SERVICE_DEPLOYMENT_NAME = "UserProfile"; @@ -47,12 +37,10 @@ public class UserProfileServer extends JsonServerServlet { public static final String CFG_MONGO_DB = "mongodb-database"; public static final String CFG_MONGO_USER = "mongodb-user"; public static final String CFG_MONGO_PSWD = "mongodb-pwd"; - public static final String CFG_MONGO_RETRY = "mongodb-retry"; + public static final String CFG_MONGO_RETRY_WRITES = "mongodb-retrywrites"; public static final String CFG_ADMIN = "admin"; - public static final String CFG_PROP_AUTH_SERVICE_URL = "auth-service-url"; - public static final String CFG_PROP_GLOBUS_URL = "globus-url"; - public static final String CFG_PROP_AUTH_INSECURE = "auth-service-url-allow-insecure"; + public static final String TRUE = "true"; private static Throwable configError = null; private static Map config = null; @@ -82,66 +70,37 @@ public static Map config() { } private String getConfig(String configName) { String ret = config().get(configName); - if (ret == null) + if (ret == null || ret.trim().isEmpty()) throw new IllegalStateException("Parameter " + configName + " is not defined in configuration"); return ret; } private final MongoController db; - private final URL authServiceUrl; - private final URL globusUrl; - private final boolean authAllowInsecure; - //END_CLASS_HEADER public UserProfileServer() throws Exception { super("UserProfile"); //BEGIN_CONSTRUCTOR - String authServiceUrlString = config().get(CFG_PROP_AUTH_SERVICE_URL); - if (authServiceUrlString == null) { - throw new IllegalStateException("Parameter " + CFG_PROP_AUTH_SERVICE_URL + " is not defined in configuration"); - } - System.out.println(UserProfileServer.class.getName() + ": " + CFG_PROP_AUTH_SERVICE_URL +" = " + authServiceUrlString); - this.authServiceUrl = new URL(authServiceUrlString); - - String globusUrlString = config().get(CFG_PROP_GLOBUS_URL); - if (globusUrlString == null) { - throw new IllegalStateException("Parameter " + CFG_PROP_GLOBUS_URL + " is not defined in configuration"); - } - System.out.println(UserProfileServer.class.getName() + ": " + CFG_PROP_GLOBUS_URL +" = " + globusUrlString); - this.globusUrl = new URL(globusUrlString); - System.out.println(UserProfileServer.class.getName() + ": " + CFG_MONGO_HOST +" = " + getConfig(CFG_MONGO_HOST)); System.out.println(UserProfileServer.class.getName() + ": " + CFG_MONGO_DB +" = " + getConfig(CFG_MONGO_DB)); System.out.println(UserProfileServer.class.getName() + ": " + CFG_ADMIN +" = " + getConfig(CFG_ADMIN)); - String authAllowInsecureString = config().get(CFG_PROP_AUTH_INSECURE); - System.out.print(UserProfileServer.class.getName() + ": " + CFG_PROP_AUTH_INSECURE +" = " + - (authAllowInsecureString == null ? "" : authAllowInsecureString)); - this.authAllowInsecure = "true".equals(authAllowInsecureString); - System.out.println(" [flag interpreted to be:"+this.authAllowInsecure + "]"); - String mongoUser = ""; boolean useMongoAuth = true; try{ mongoUser = getConfig(CFG_MONGO_USER); } catch (Exception e) { useMongoAuth = false; } - + + final boolean mongoRetryWrites = TRUE.equals(getConfig(CFG_MONGO_RETRY_WRITES)); + if(useMongoAuth) { - db = new MongoController( - getConfig(CFG_MONGO_HOST), - getConfig(CFG_MONGO_DB), - mongoUser, - getConfig(CFG_MONGO_PSWD) - ); + db = new MongoController(getConfig(CFG_MONGO_HOST), getConfig(CFG_MONGO_DB), + mongoUser, getConfig(CFG_MONGO_PSWD), mongoRetryWrites); } else { - db = new MongoController( - getConfig(CFG_MONGO_HOST), - getConfig(CFG_MONGO_DB)); + db = new MongoController(getConfig(CFG_MONGO_HOST), getConfig(CFG_MONGO_DB), mongoRetryWrites); } - //END_CONSTRUCTOR } @@ -263,40 +222,6 @@ public void updateUserProfile(SetUserProfileParams p, AuthToken authPart, RpcCon db.updateProfile(p.getProfile()); //END update_user_profile } - - /** - *

Original spec-file function name: lookup_globus_user

- *
-     * 
- * @param usernames instance of list of original type "username" - * @return parameter "users" of mapping from original type "username" to type {@link us.kbase.userprofile.GlobusUser GlobusUser} - */ - @JsonServerMethod(rpc = "UserProfile.lookup_globus_user", async=true) - public Map lookupGlobusUser(List usernames, AuthToken authPart, RpcContext jsonRpcContext) throws Exception { - Map returnVal = null; - //BEGIN lookup_globus_user - - ConfigurableAuthService authService = new ConfigurableAuthService( - new AuthConfig() - .withKBaseAuthServerURL(authServiceUrl) - .withGlobusAuthURL(globusUrl) - .withAllowInsecureURLs(authAllowInsecure) - ); - - Map data = authService.fetchUserDetail(usernames, authPart); - returnVal = new HashMap(data.size()); - - for (UserDetail ud : data.values()) { - if(ud!=null) { - GlobusUser gu = new GlobusUser().withUserName(ud.getUserName()); - if(ud.getEmail()!=null) { gu.setEmail(ud.getEmail()); } - if(ud.getFullName()!=null) { gu.setFullName(ud.getFullName()); } - returnVal.put(ud.getUserName(), gu); - } - } - //END lookup_globus_user - return returnVal; - } @JsonServerMethod(rpc = "UserProfile.status") public Map status() { Map returnVal = null; @@ -304,9 +229,15 @@ public Map status() { returnVal = new LinkedHashMap(); returnVal.put("state", "OK"); returnVal.put("message", ""); - returnVal.put("version", version); + returnVal.put("version", VERSION); returnVal.put("git_url", gitUrl); - returnVal.put("git_commit_hash", gitCommitHash); + // the git commit can't be correct, as the server needs to be recompiled on the current + // commit, which doesn't happen curing the build. + //returnVal.put("git_commit_hash", gitCommitHash); + @SuppressWarnings("unused") + final String foo = gitCommitHash; + @SuppressWarnings("unused") + final String bar = version; //END_STATUS return returnVal; } diff --git a/src/us/kbase/userprofile/test/FullServerTest.java b/src/test/java/us/kbase/test/userprofile/FullServerTest.java similarity index 60% rename from src/us/kbase/userprofile/test/FullServerTest.java rename to src/test/java/us/kbase/test/userprofile/FullServerTest.java index 09dd927..63ede21 100644 --- a/src/us/kbase/userprofile/test/FullServerTest.java +++ b/src/test/java/us/kbase/test/userprofile/FullServerTest.java @@ -1,14 +1,18 @@ -package us.kbase.userprofile.test; +package us.kbase.test.userprofile; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.io.File; import java.lang.reflect.Field; import java.net.URL; +import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import java.util.Map; +import com.google.common.collect.ImmutableMap; import org.apache.commons.io.FileUtils; import org.ini4j.Ini; import org.ini4j.Profile.Section; @@ -21,12 +25,11 @@ import us.kbase.auth.AuthConfig; import us.kbase.common.service.UObject; - +import us.kbase.testutils.controllers.mongo.MongoController; import us.kbase.userprofile.FilterParams; import us.kbase.userprofile.SetUserProfileParams; import us.kbase.userprofile.User; import us.kbase.userprofile.UserProfile; -import us.kbase.userprofile.GlobusUser; import us.kbase.userprofile.UserProfileClient; import us.kbase.userprofile.UserProfileServer; @@ -35,6 +38,7 @@ public class FullServerTest { private static File tempDir; private static UserProfileServer SERVER; + private static MongoController MONGO; private static UserProfileClient CLIENT; private static UserProfileClient USR1_CLIENT; private static UserProfileClient ADMIN_CLIENT; @@ -80,13 +84,33 @@ public void testFilterUsers() throws Exception { @Test public void testBasicPath() throws Exception { + List profile = CLIENT.getUserProfile(Arrays.asList(USER1_NAME)); + assertEquals(1, profile.size()); + assertNull(profile.get(0)); + + // Test the updateProfile function on a non-existent user + UserProfile nonExistP = new UserProfile().withUser(new User() + .withUsername("not_exist") + .withRealname("real real name") + .withThumbnail("thumb thumb nail")) + .withProfile(new UObject(ImmutableMap.of("test", "nonsense"))); + ADMIN_CLIENT.updateUserProfile(new SetUserProfileParams().withProfile(nonExistP)); + + List profiles0 = CLIENT.getUserProfile(Arrays.asList("not_exist")); + assertEquals(1, profiles0.size()); + UserProfile ret0 = profiles0.get(0); + assertEquals("not_exist", ret0.getUser().getUsername()); + assertEquals("real real name", ret0.getUser().getRealname()); + assertEquals("thumb thumb nail", ret0.getUser().getThumbnail()); + assertEquals("nonsense", ret0.getProfile().asMap().get("test").asScalar()); + // User1 creates a profile String jsonProfile1 = "{\"stuff\":\"yeah\"}"; - UserProfile p = new UserProfile() - .withUser(new User() - .withUsername(USER1_NAME) - .withRealname("User One")) - .withProfile(UObject.fromJsonString(jsonProfile1)); + UserProfile p = new UserProfile().withUser(new User() + .withUsername(USER1_NAME) + .withRealname("User One") + .withThumbnail("User One Thumbnail")) + .withProfile(UObject.fromJsonString(jsonProfile1)); USR1_CLIENT.setUserProfile(new SetUserProfileParams().withProfile(p)); // Profile is visible to an anonymous user @@ -94,6 +118,8 @@ public void testBasicPath() throws Exception { assertEquals(1, profiles.size()); UserProfile ret = profiles.get(0); assertEquals(USER1_NAME, ret.getUser().getUsername()); + assertEquals("User One", ret.getUser().getRealname()); + assertEquals("User One Thumbnail", ret.getUser().getThumbnail()); assertEquals("yeah", ret.getProfile().asMap().get("stuff").asScalar()); // Admin updates profile @@ -113,12 +139,13 @@ public void testBasicPath() throws Exception { assertEquals("yeah2", ret2.getProfile().asMap().get("stuff").asScalar()); - // User1 adds a field to the profile - String jsonProfileUpdate = "{\"new_stuff\":\"yeah\"}"; - UserProfile p3 = new UserProfile() - .withUser(new User() - .withUsername(USER1_NAME)) - .withProfile(UObject.fromJsonString(jsonProfileUpdate)); + // User1 adds fields to the profile + String jsonProfileUpdate = "{\"new_stuff\":\"yeah\", \"complex_stuff\":{\"yee\": \"haw\"}}"; + UserProfile p3 = new UserProfile().withUser(new User() + .withUsername(USER1_NAME) + .withRealname("User One") + .withThumbnail("User One Thumbnail updated")) + .withProfile(UObject.fromJsonString(jsonProfileUpdate)); USR1_CLIENT.updateUserProfile(new SetUserProfileParams().withProfile(p3)); // Profile is updated as expected @@ -126,8 +153,11 @@ public void testBasicPath() throws Exception { assertEquals(1, profiles3.size()); UserProfile ret3 = profiles3.get(0); assertEquals(USER1_NAME, ret3.getUser().getUsername()); + assertEquals("User One", ret3.getUser().getRealname()); + assertEquals("User One Thumbnail updated", ret3.getUser().getThumbnail()); assertEquals("yeah2", ret3.getProfile().asMap().get("stuff").asScalar()); assertEquals("yeah", ret3.getProfile().asMap().get("new_stuff").asScalar()); + assertEquals("haw", ret3.getProfile().asMap().get("complex_stuff").asMap().get("yee").asScalar()); // Make sure that when we filter users, we get at least this one hit. @@ -137,82 +167,54 @@ public void testBasicPath() throws Exception { assertTrue(0 results = USR1_CLIENT.lookupGlobusUser(Arrays.asList("msneddon", USER1_NAME)); - assertEquals(2, results.size()); - assertNotNull(results.get(USER1_NAME).getEmail()); - assertNotNull(results.get(USER1_NAME).getFullName()); - assertNotNull(results.get("msneddon").getFullName()); - assertTrue(0Original spec-file type: GlobusUser

- * - * - */ -@JsonInclude(JsonInclude.Include.NON_NULL) -@Generated("com.googlecode.jsonschema2pojo") -@JsonPropertyOrder({ - "email", - "fullName", - "userName" -}) -public class GlobusUser { - - @JsonProperty("email") - private String email; - @JsonProperty("fullName") - private String fullName; - @JsonProperty("userName") - private String userName; - private Map additionalProperties = new HashMap(); - - @JsonProperty("email") - public String getEmail() { - return email; - } - - @JsonProperty("email") - public void setEmail(String email) { - this.email = email; - } - - public GlobusUser withEmail(String email) { - this.email = email; - return this; - } - - @JsonProperty("fullName") - public String getFullName() { - return fullName; - } - - @JsonProperty("fullName") - public void setFullName(String fullName) { - this.fullName = fullName; - } - - public GlobusUser withFullName(String fullName) { - this.fullName = fullName; - return this; - } - - @JsonProperty("userName") - public String getUserName() { - return userName; - } - - @JsonProperty("userName") - public void setUserName(String userName) { - this.userName = userName; - } - - public GlobusUser withUserName(String userName) { - this.userName = userName; - return this; - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @JsonAnySetter - public void setAdditionalProperties(String name, Object value) { - this.additionalProperties.put(name, value); - } - - @Override - public String toString() { - return ((((((((("GlobusUser"+" [email=")+ email)+", fullName=")+ fullName)+", userName=")+ userName)+", additionalProperties=")+ additionalProperties)+"]"); - } - -} diff --git a/src/us/kbase/userprofile/MongoController.java b/src/us/kbase/userprofile/MongoController.java deleted file mode 100644 index af77393..0000000 --- a/src/us/kbase/userprofile/MongoController.java +++ /dev/null @@ -1,285 +0,0 @@ -package us.kbase.userprofile; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; - - - -/* -import org.apache.commons.lang3.StringUtils;*/ - -import us.kbase.common.service.UObject; - -import com.fasterxml.jackson.databind.JsonNode; -import com.mongodb.BasicDBObject; -import com.mongodb.DB; -import com.mongodb.DBCollection; -import com.mongodb.DBCursor; -import com.mongodb.DBObject; -import com.mongodb.MongoClient; -import com.mongodb.MongoClientOptions; -import com.mongodb.MongoCredential; -import com.mongodb.ServerAddress; -import com.mongodb.util.JSON; - -public class MongoController { - - private static final String COL_PROFILES = "profiles"; - - private final DBCollection profiles; - //private final MongoCollection jProfiles; - - public MongoController(final String host, final String database) { - - final DB db = getDB(host, database, null, null); - profiles = db.getCollection(COL_PROFILES); - //jProfiles = jongo.getCollection(COL_PROFILES); - ensureIndex(); - /*System.out.println(getProfile("mike3")); - - User u = new User().withUsername("mike3").withRealname("oh yeah"); - Map m = new HashMap(); - m.put("email", "test@test.com"); - //m.put("stuff", "things"); - UserProfile up = new UserProfile().withUser(u) - .withProfile(new UObject(m)); - setProfile(up); - System.out.println(getProfile("mike2")); - - System.out.println("filtering..."); - filterUsers("ik");*/ - } - - public MongoController(final String host, final String database, - final String mongoUser, final String mongoPswd) { - final DB db = getDB(host, database, mongoUser, mongoPswd); - profiles = db.getCollection(COL_PROFILES); - //jProfiles = jongo.getCollection(COL_PROFILES); - ensureIndex(); - } - - private DB getDB(final String host, final String db, final String user, final String pwd) { - // TODO update to non-deprecated APIs - final MongoClient cli; - if (user != null) { - final MongoCredential creds = MongoCredential.createCredential( - user, db, pwd.toCharArray()); - // unclear if and when it's safe to clear the password - cli = new MongoClient(new ServerAddress(host), creds, - MongoClientOptions.builder().build()); - } else { - cli = new MongoClient(new ServerAddress(host)); - } - return cli.getDB(db); - } - - - public List filterUsers(String filter) { - - if(filter.trim().isEmpty()) { - //return all - DBCursor cursor = profiles.find(new BasicDBObject(), - new BasicDBObject().append("user",1)); - List users = new ArrayList(cursor.count()); - while(cursor.hasNext()) { - User u = new User(); - DBObject d = (DBObject)cursor.next().get("user"); - if(!d.containsField("username")) continue; - u.setUsername(d.get("username").toString()); - if(d.containsField("realname")) { - if(d.get("realname")!=null) { - u.setRealname(d.get("realname").toString()); - } - } - if(d.containsField("thumbnail")) { - if(d.get("thumbnail")!=null) { - u.setThumbnail(d.get("thumbnail").toString()); - } - } - //System.out.println(u); - users.add(u); - } - return users; - } - - // for now we do text search here instead of on the DB side - // todo: see if we can install a text search index - DBCursor cursor = profiles.find(new BasicDBObject(), - new BasicDBObject().append("user",1)); - List users = new ArrayList(cursor.count()); - String [] terms = filter.split("\\s+"); - while(cursor.hasNext()) { - User u = new User(); - DBObject d = (DBObject)cursor.next().get("user"); - if(!d.containsField("username")) continue; - String uname = d.get("username").toString(); - u.setUsername(uname); - boolean add = true; - for(int i=0; i> fields = profileNode.fields(); - while(fields.hasNext()) { - Entry e= fields.next(); - update.put("profile."+e.getKey(), JSON.parse(e.getValue().toString())); - } - } else { - throw new RuntimeException("Profile must be an object if defined."); - } - } - System.out.println(update); - profiles.update( - new BasicDBObject("user.username",up.getUser().getUsername()), - new BasicDBObject("$set",update)); - } else { - DBObject user = new BasicDBObject("username",up.getUser().getUsername()) - .append("realname", up.getUser().getRealname()) - .append("thumbnail", up.getUser().getThumbnail()); - - DBObject profile = new BasicDBObject("user",user); - if(up.getProfile()!=null) { - if(up.getProfile().asJsonNode().isObject()) - profile.put("profile", JSON.parse(up.getProfile().asJsonNode().toString())); - else { - throw new RuntimeException("Profile must be an object if defined."); - } - } else { - profile.put("profile", null); - } - profiles.insert(profile); - } - } - - -} diff --git a/test/test.cfg.example b/test.cfg.example similarity index 55% rename from test/test.cfg.example rename to test.cfg.example index d29e5bf..b0718d3 100644 --- a/test/test.cfg.example +++ b/test.cfg.example @@ -2,13 +2,10 @@ [UserProfile] -test.temp-dir = test/temp -test.remove-temp-dir = 1 +test.mongo-exe-path=/path/to/mongod -test.mongodb-host = localhost -test.mongodb-database = user_profile_test - -test.admin = kbuserprofileadmin +test.temp-dir = user_profile_test_temp +test.remove-temp-dir = true # admin token is required test.admin-token = xxx @@ -16,8 +13,6 @@ test.admin-token = xxx # token is required test.usr1-token = xxx - test.auth-service-url = https://ci.kbase.us/services/auth/api/legacy/KBase -test.globus-url = https://ci.kbase.us/services/auth/api/legacy/globus #test.auth-service-url-allow-insecure = false diff --git a/test/cfg_to_runner.py b/test/cfg_to_runner.py deleted file mode 100755 index f8dfc40..0000000 --- a/test/cfg_to_runner.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python -''' -Created on Aug 30, 2013 - -@author: gaprice@lbl.gov -''' -from configobj import ConfigObj -import os -import sys - -ANT = 'ant' - -CFG_SECTION = 'UserProfile' - -CONFIG_OPTS = ['test.temp-dir', - 'test.remove-temp-dir', - 'test.mongodb-host', - 'test.mongodb-database', - 'test.admin', - 'test.admin-pwd', - 'test.admin-token', - 'test.usr1', - 'test.usr1-pwd', - 'test.usr1-token', - 'test.auth-service-url', - 'test.globus-url', - 'test.auth-service-url-allow-insecure' - ] - - -def write_runner(out, ant_target): - with open(out, 'w') as run: - run.write('# Generated file - do not check into git\n') -# run.write('cd ..\n') - run.write(ANT + ' ' + ant_target) - for o in CONFIG_OPTS: - if o in testcfg: - run.write(' -D' + o + '=' + testcfg[o]) - run.write('\n') - os.chmod(out, 0755) - print 'Writing test runner with target "' + ant_target + '" to: ' + out - - -if __name__ == '__main__': - d, _ = os.path.split(os.path.abspath(__file__)) - fn = 'test.cfg' - if len(sys.argv) > 1: - fn = sys.argv[1] - fn = os.path.join(d, fn) - if not os.path.isfile(fn): - print 'No such config file ' + fn + '. Halting.' - sys.exit(1) - print 'Using test config file ' + fn - out_run_tests = os.path.join(d, 'run_tests.sh') - out_run_script_tests = os.path.join(d, 'run_script_tests.sh') - cfg = ConfigObj(fn) - try: - testcfg = cfg[CFG_SECTION] - except KeyError as ke: - print 'Test config file ' + fn + ' is missing section ' +\ - CFG_SECTION + '. Halting.' - sys.exit(1) - - write_runner(out_run_tests, 'test') - #write_runner(out_run_script_tests, 'test-scripts') - - #create a copy of the cfg file in the test/scripts/files dir for script - # tests -mike - #scriptcfgfile = os.path.join(d, 'scripts', 'files', 'test.cfg.copy') - #with open(scriptcfgfile, 'w') as copyfile: - # cfg.write(copyfile)