diff --git a/.gitignore b/.gitignore index 07f7b0c567..0130575096 100644 --- a/.gitignore +++ b/.gitignore @@ -77,10 +77,10 @@ var/ .installed.cfg *.egg webpack-stats.json -api-tests/.envrc -functional-tests/.envrc +app/backend/*.sh app/backend/.env *.secret_env +app/backend/.pip # PyInstaller and Pip install files # Usually these files are written by a python script from a template @@ -133,9 +133,12 @@ functional-tests/src/test/groovy/testtamplet.txt #virtual env /deactivate +venv .venv venv .direnv +app/backend/.env +*.secret_env # Frontend files frontend/webpack-stats.json diff --git a/Jenkinsfile b/Jenkinsfile index 0d92e57f1e..f1c0bf682d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,565 +1,458 @@ -// Jenkinsfile (Scripted Pipeline) - -/* Gotchas: - - PodTemplate name/label has to be unique to ensure proper caching/validation - - https://gist.github.com/matthiasbalke/3c9ecccbea1d460ee4c3fbc5843ede4a - - Libraries: - - https://github.com/BCDevOps/jenkins-pipeline-shared-lib - - http://github-api.kohsuke.org/apidocs/index.html -*/ -import hudson.model.Result; -import jenkins.model.CauseOfInterruption.UserInterruption; -import org.kohsuke.github.* -import bcgov.OpenShiftHelper -import bcgov.GitHubHelper - - -// Print stack trace of error -@NonCPS -private static String stackTraceAsString(Throwable t) { - StringWriter sw = new StringWriter(); - t.printStackTrace(new PrintWriter(sw)); - return sw.toString() -} - - -// Notify stage status and pass to Jenkins-GitHub library -void notifyStageStatus (Map context, String name, String status) { - GitHubHelper.createCommitStatus( - this, - context.pullRequest.head, - status, - "${env.BUILD_URL}", - "Stage '${name}'", - "stages/${name.toLowerCase()}" - ) -} - - -// Check if a stage is enabled (true|false in context) -boolean isEnabled (Map context, String stageName) { - def stageOpt =(context?.stages?:[:])[stageName] - return (stageOpt == null || stageOpt == true) -} +#!groovy +import groovy.json.JsonOutput +import bcgov.GitHubHelper -// Python tests can run early an in the container if Code Quality is disabled -void unitTestsPython (Map context, boolean isQuick=false) { - boolean doCodeQuality = isEnabled( context, 'Code Quality' ) - if (doCodeQuality && !isQuick) { - try { - echo "Running tests with artifact stash for SonarQube" - sh script: '''#!/usr/bin/container-entrypoint /bin/sh - cd /opt/app-root/src/backend - DATABASE_ENGINE=sqlite DEBUG=False TEMPLATE_DEBUG=False python manage.py test -c nose.cfg - ''' - sh script: '''#!/usr/bin/container-entrypoint /bin/sh - cp /opt/app-root/src/backend/nosetests.xml ./ - cp /opt/app-root/src/backend/coverage.xml ./ - ''' - stash includes: 'nosetests.xml,coverage.xml', name: 'coverage' - } finally { - stash includes: 'nosetests.xml,coverage.xml', name: 'coverage' - junit 'nosetests.xml' - } - } else if (!doCodeQuality && isQuick){ - echo "Running short tests w/o artifact stash since SonarQube is disabled" - String deploymentConfigName = "gwells${context.deployments['dev'].dcSuffix}" - String projectName = context.deployments['dev'].projectName - String podName = openshift.withProject(projectName){ - return openshift.selector('pod', ['deploymentconfig':deploymentConfigName]).objects()[1].metadata.name +pipeline { + environment { + + APP_NAME = "gwells" + REPOSITORY = 'https://www.github.com/bcgov/gwells.git' + + // TOOLS_PROJECT is where images are built + TOOLS_PROJECT = "moe-gwells-tools" + + // DEV_PROJECT is the project where individual development environments are spun up + // for example: a pull request PR-999 will result in gwells-dev-pr-999.pathfinder.gov.bc.ca + DEV_PROJECT = "moe-gwells-dev" + DEV_SUFFIX = "dev" + + // TEST_PROJECT contains the test deployment. The test image is a candidate for promotion to prod. + TEST_PROJECT = "moe-gwells-test" + TEST_SUFFIX = "staging" + + // PROD_PROJECT is the prod deployment. + // New production images can be deployed by tagging an existing "test" image as "prod". + PROD_PROJECT = "moe-gwells-prod" + PROD_SUFFIX= "production" + + // PR_NUM is the pull request number e.g. 'pr-4' + PR_NUM = "${env.JOB_BASE_NAME}".toLowerCase() + } + agent any + stages { + + // the Start Pipeline stage will process and apply OpenShift build templates which will create + // buildconfigs and an imagestream for built images. + // each pull request gets its own buildconfig but all new builds are pushed to a single imagestream, + // to be tagged with the pull request number. + // e.g.: gwells-app:pr-999 + stage('Prepare Templates') { + steps { + script { + echo "Cancelling previous builds..." + timeout(10) { + abortAllPreviousBuildInProgress(currentBuild) + } + echo "Previous builds cancelled" + + openshift.withCluster() { + openshift.withProject(TOOLS_PROJECT) { + + // Process db and app template into list objects + // - variable substitution + echo "Processing build templates" + def dbtemplate = openshift.process("-f", + "openshift/postgresql.bc.json", + "ENV_NAME=${DEV_SUFFIX}" + ) + // + def buildtemplate = openshift.process("-f", + "openshift/backend.bc.json", + "ENV_NAME=${DEV_SUFFIX}", + "NAME_SUFFIX=-${DEV_SUFFIX}-${PR_NUM}", + "APP_IMAGE_TAG=${PR_NUM}", + "SOURCE_REPOSITORY_URL=${REPOSITORY}", + "SOURCE_REPOSITORY_REF=pull/${CHANGE_ID}/head" + ) + + // Apply oc list objects + // - add docker image reference as tag in gwells-postgresql + echo "Preparing database imagestream" + echo " \$ oc process -f openshift/postgresql.bc.json -p ENV_NAME=${DEV_SUFFIX} | oc apply -n moe-gwells-tools -f -" + openshift.apply(dbtemplate) + // - add docker image reference as tag in gwells-application + // - create build config + echo "Preparing backend imagestream and buildconfig" + echo " \$ oc process -f openshift/backend.bc.json -p ENV_NAME=${DEV_SUFFIX} -p NAME_SUFFIX=-${DEV_SUFFIX}-${PR_NUM} -p APP_IMAGE_TAG=${PR_NUM} -p SOURCE_REPOSITORY_URL=${REPOSITORY} -p SOURCE_REPOSITORY_REF=pull/${CHANGE_ID}/head | oc apply -n moe-gwells-tools -f -" + openshift.apply(buildtemplate) + } + } } - sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ - cd /opt/app-root/src/backend; \ - DATABASE_ENGINE=sqlite DEBUG=False TEMPLATE_DEBUG=False python manage.py test -c nose.cfg \ - '" - } else { - echo "Python unit tests are being skipped at this stage" - echo "doCodeQuality = ${doCodeQuality}" - echo "isQuick = ${isQuick}" + } } -} -void unitTestsPythonQuick (Map context) {unitTestsPython (context, true)} -void unitTestsPythonFull (Map context) {unitTestsPython (context, false)} - - -/* _Stage wrapper: - - runs stages against true|false in map context - - receives stages defined separately in closures (body) - - catches errors and provides output -*/ -def _stage(String name, Map context, boolean retry=0, boolean withCommitStatus=true, Closure body) { - timestamps { - echo "Running Stage '${name}'" - if (isEnabled(context,name)){ - stage(name) { - waitUntil { - notifyStageStatus(context, name, 'PENDING') - boolean isDone=false - try{ - body() - isDone=true - notifyStageStatus(context, name, 'SUCCESS') - echo "Completed Stage '${name}'" - }catch (ex){ - notifyStageStatus(context, name, 'FAILURE') - echo "${stackTraceAsString(ex)}" - def inputAction = input( - message: "This step (${name}) has failed. See error above.", - ok: 'Confirm', - parameters: [ - choice( - name: 'action', - choices: 'Re-run\nIgnore', - description: 'What would you like to do?' - ) - ] - ) - if ('Ignore'.equalsIgnoreCase(inputAction)){ - isDone=true - } - } - return isDone - } //end waitUntil - } //end Stage - }else{ - stage(name) { - echo 'Skipping' + + // the Build stage runs unit tests and builds files. an image will be outputted to the app's imagestream + // builds use the source to image strategy. See /app/.s2i/assemble for image build script + stage('Build (with tests)') { + steps { + script { + openshift.withCluster() { + openshift.withProject(TOOLS_PROJECT) { + echo "Running unit tests and building images..." + echo "This may take several minutes. Logs are not forwarded to Jenkins by default (at this time)." + echo "Additional logs can be found by monitoring builds in ${TOOLS_PROJECT}" + + // Select appropriate buildconfig + def appBuild = openshift.selector("bc", "${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}") + // temporarily set ENABLE_DATA_ENTRY=True during testing because False currently leads to a failing unit test + echo "Building" + echo " \$ oc start-build -n moe-gwells-tools ${APP_NAME}-${DEV_SUFFIX}-${PR_NUM} --wait --env=ENABLE_DATA_ENTRY=true --follow=true" + appBuild.startBuild("--wait", "--env=ENABLE_DATA_ENTRY=True").logs("-f") } + } } + } } -} - - -/* Project and build settings - Includes: - - build (*.bc) and config templates (*.dc) - - stage names and enabled status (true|false) -*/ -Map context = [ - 'name': 'gwells', - 'uuid' : "${env.JOB_BASE_NAME}-${env.BUILD_NUMBER}-${env.CHANGE_ID}", - 'env': [ - 'dev':[:], - 'test':[ - 'params':[ - 'host':'gwells-test.pathfinder.gov.bc.ca', - 'DB_PVC_SIZE':'5Gi' - ] - ], - 'prod':[ - 'params':[ - 'host':'gwells-prod.pathfinder.gov.bc.ca', - 'DB_PVC_SIZE':'5Gi' - ] - ] - ], - 'templates': [ - 'build':[ - ['file':'openshift/postgresql.bc.json'], - ['file':'openshift/backend.bc.json'] - ], - 'deployment':[ - [ - 'file':'openshift/postgresql.dc.json', - 'params':[ - 'DATABASE_SERVICE_NAME':'gwells-pgsql${deploy.dcSuffix}', - 'IMAGE_STREAM_NAMESPACE':'', - 'IMAGE_STREAM_NAME':'gwells-postgresql${deploy.dcSuffix}', - 'IMAGE_STREAM_VERSION':'${deploy.envName}', - 'POSTGRESQL_DATABASE':'gwells', - 'VOLUME_CAPACITY':'${env[DEPLOY_ENV_NAME]?.params?.DB_PVC_SIZE?:"1Gi"}' - ] - ], - [ - 'file':'openshift/backend.dc.json', - 'params':[ - 'HOST':'${env[DEPLOY_ENV_NAME]?.params?.host?:("gwells" + deployments[DEPLOY_ENV_NAME].dcSuffix + "-" + deployments[DEPLOY_ENV_NAME].projectName + ".pathfinder.gov.bc.ca")}' - ] - ] - ] - ], - stages:[ - 'Load Fixtures': true, - 'API Tests': true, - 'Functional Tests': true, - 'Unit Tests': true, - 'Code Quality': false, - 'ZAP Security Scan': false - ], - pullRequest:[ - 'id': env.CHANGE_ID, - 'head': GitHubHelper.getPullRequestLastCommitId(this) - ] -] - - -/* Jenkins properties can be set on a pipeline-by-pipeline basis - See Jenkins' Pipeline Systax for generation - Globally equivalent to Jenkins > Manage Jenkins > Configure System - https://jenkins.io/doc/pipeline/steps/workflow-multibranch/#properties-set-job-properties -*/ -properties([ - buildDiscarder( - logRotator( - artifactDaysToKeepStr: '', - artifactNumToKeepStr: '', - daysToKeepStr: '', - numToKeepStr: '5' - ) - ), - durabilityHint( - 'PERFORMANCE_OPTIMIZED' - ), - disableResume() -]) - - -/* Prepare stage - - abort any existing builds - - echo pull request number -*/ -stage('Prepare') { - abortAllPreviousBuildInProgress(currentBuild) -} -/* Build stage - - applying OpenShift build configs - - creating OpenShift imagestreams, annotations and builds - - build time optimizations (e.g. image reuse, build scheduling/readiness) -*/ -_stage('Build', context) { - node('master') { - checkout scm - new OpenShiftHelper().build(this, context) - if ("master".equalsIgnoreCase(env.CHANGE_TARGET)) { - new OpenShiftHelper().prepareForCD(this, context) - new OpenShiftHelper().waitUntilEnvironmentIsReady(this, context, 'dev') - } - deleteDir() - } -} //end stage - - -/* Continuous Integration (CI) - For feature branches merging into a release branch - || Deploy and Load Fixtures (sets isDeployed and isFixtured=true) - || Unit tests (sets isUnitTested=true) - -> || Python tests - || Node tests - || ZAP Security Scan (executes on isDeployed) - || Functional tests (executes on isFixtured) - || API tests (executes on isFixtured) - || Code quality (executes on isUnitTested) -*/ -boolean isDeployed = false -boolean isFixtured = false -boolean isUnitTested = false -parallel ( - "Deploy and Load Fixtures" : { - - _stage('Deploy', context) { - node('master') { - new OpenShiftHelper().deploy(this, context, 'dev') - String deploymentConfigName = "gwells${context.deployments['dev'].dcSuffix}" - String projectName = context.deployments['dev'].projectName - openshift.withProject(projectName) { - // get list of pods for the new deployment - def latestDeployment = openshift.selector('dc', deploymentConfigName).object().status.latestVersion - def pods = openshift.selector('pod', [deployment: "${deploymentConfigName}-${latestDeployment}"]) - - pods.untilEach(1) { - return it.object().status.containerStatuses.every { - it.ready - } - } + // the Deploy to Dev stage creates a new dev environment for the pull request (if necessary), tags the newly built + // application image into that environment, and monitors the newest deployment for pods/containers to + // report back as ready. + stage('Deploy to dev') { + steps { + script { + openshift.withCluster() { + openshift.withProject(DEV_PROJECT) { + // Process postgres deployment config (sub in vars, create list items) + echo " \$ oc process -f openshift/postgresql.dc.json -p DATABASE_SERVICE_NAME=gwells-pgsql-${DEV_SUFFIX}-${PR_NUM} -p IMAGE_STREAM_NAMESPACE='' -p IMAGE_STREAM_NAME=gwells-postgresql-${DEV_SUFFIX}-${PR_NUM} -p IMAGE_STREAM_VERSION=${DEV_SUFFIX} -p NAME_SUFFIX=-${DEV_SUFFIX}-${PR_NUM} -p POSTGRESQL_DATABASE=gwells -p VOLUME_CAPACITY=1Gi | oc apply -n moe-gwells-dev -f -" + def deployDBTemplate = openshift.process("-f", + "openshift/postgresql.dc.json", + "DATABASE_SERVICE_NAME=gwells-pgsql-${DEV_SUFFIX}-${PR_NUM}", + "IMAGE_STREAM_NAMESPACE=''", + "IMAGE_STREAM_NAME=gwells-postgresql-${DEV_SUFFIX}-${PR_NUM}", + "IMAGE_STREAM_VERSION=${DEV_SUFFIX}", + "NAME_SUFFIX=-${DEV_SUFFIX}-${PR_NUM}", + "POSTGRESQL_DATABASE=gwells", + "VOLUME_CAPACITY=1Gi" + ) + + // Process postgres deployment config (sub in vars, create list items) + echo " \$ oc process -f openshift/backend.dc.json -p ENV_NAME=${DEV_SUFFIX} -p NAME_SUFFIX=-${DEV_SUFFIX}-${PR_NUM} | oc apply -n moe-gwells-dev -f -" + echo "Processing deployment config for pull request ${PR_NUM}" + def deployTemplate = openshift.process("-f", + "openshift/backend.dc.json", + "ENV_NAME=${DEV_SUFFIX}", + "HOST=${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}.pathfinder.gov.bc.ca", + "NAME_SUFFIX=-${DEV_SUFFIX}-${PR_NUM}" + ) + + // some objects need to be copied from a base secret or configmap + // these objects have an annotation "as-copy-of" in their object spec (e.g. an object in backend.dc.json) + echo "Creating configmaps and secrets objects" + List newObjectCopies = [] + + for (o in (deployTemplate + deployDBTemplate)) { + + // only perform this operation on objects with 'as-copy-of' + def sourceName = o.metadata && o.metadata.annotations && o.metadata.annotations['as-copy-of'] + if (sourceName && sourceName.length() > 0) { + def selector = openshift.selector("${o.kind}/${sourceName}") + if (selector.count() == 1) { + + // create a copy of the object and add it to the new list of objects to be applied + Map copiedModel = selector.object(exportable:true) + copiedModel.metadata.name = o.metadata.name + echo "[as-copy-of] Copying ${o.kind} ${o.metadata.name}" + newObjectCopies.add(copiedModel) + } + } + } + + + echo "Applying deployment config for pull request ${PR_NUM} on ${DEV_PROJECT}" + + // apply the templates, which will create new objects or modify existing ones as necessary. + // the copies of base objects (secrets, configmaps) are also applied. + openshift.apply(deployTemplate).label(['app':"gwells-${DEV_SUFFIX}-${PR_NUM}", 'app-name':"${APP_NAME}", 'env-name':"${DEV_SUFFIX}"], "--overwrite") + openshift.apply(deployDBTemplate).label(['app':"gwells-${DEV_SUFFIX}-${PR_NUM}", 'app-name':"${APP_NAME}", 'env-name':"${DEV_SUFFIX}"], "--overwrite") + openshift.apply(newObjectCopies).label(['app':"gwells-${DEV_SUFFIX}-${PR_NUM}", 'app-name':"${APP_NAME}", 'env-name':"${DEV_SUFFIX}"], "--overwrite") + echo "Successfully applied deployment configs for ${PR_NUM}" + + // promote the newly built image to DEV + echo "Tagging new image to DEV imagestream." + openshift.tag("${TOOLS_PROJECT}/gwells-application:${PR_NUM}", "${DEV_PROJECT}/gwells-${DEV_SUFFIX}-${PR_NUM}:dev") // todo: clean up labels/tags + openshift.tag("${TOOLS_PROJECT}/gwells-postgresql:dev", "${DEV_PROJECT}/gwells-postgresql-${DEV_SUFFIX}-${PR_NUM}:dev") // todo: clean up labels/tags + + // post a notification to Github that this pull request is being deployed + def targetURL = "https://${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}.pathfinder.gov.bc.ca/gwells" + def ghDeploymentId = new GitHubHelper().createDeployment(this, "pull/${env.CHANGE_ID}/head", ['environment':"${DEV_SUFFIX}", 'task':"deploy:pull:${env.CHANGE_ID}"]) + new GitHubHelper().createDeploymentStatus(this, ghDeploymentId, 'PENDING', ['targetUrl':"${targetURL}"]) + + // monitor the deployment status and wait until deployment is successful + echo "Waiting for deployment to dev..." + def newVersion = openshift.selector("dc", "${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}").object().status.latestVersion + def pods = openshift.selector('pod', [deployment: "${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}-${newVersion}"]) + + // wait until each container in this deployment's pod reports as ready + timeout(15) { + pods.untilEach(2) { + return it.object().status.containerStatuses.every { + it.ready + } } - isDeployed = true + } + + echo "Deployment successful!" + echo "Loading fixtures" + def firstPod = pods.objects()[0].metadata.name + openshift.exec(firstPod, "--", "bash -c '\ + cd /opt/app-root/src/backend; \ + python manage.py loaddata \ + gwells-codetables.json \ + wellsearch-codetables.json \ + registries-codetables.json \ + registries.json \ + aquifers.json \ + wellsearch.json.gz; \ + python manage.py createinitialrevisions'") + + new GitHubHelper().createDeploymentStatus(this, ghDeploymentId, 'SUCCESS', ['targetUrl':"${targetURL}"]) + } + } } + } + } - _stage('Load Fixtures', context) { - node('master'){ - parallel ( - "Load Fixtures": { - String deploymentConfigName = "gwells${context.deployments['dev'].dcSuffix}" - String projectName = context.deployments['dev'].projectName - String podName = openshift.withProject(projectName){ - return openshift.selector('pod', ['deploymentconfig':deploymentConfigName]).objects()[0].metadata.name - } - - /* All of these commands could be run in one go, and be more performant, but then - it becomes difficult to see which on of the steps failed. Instead, each step is - executed by itself. */ - sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ - cd /opt/app-root/src/backend; \ - python manage.py migrate \ - '" - sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ - cd /opt/app-root/src/backend; \ - python manage.py loaddata gwells-codetables.json \ - '" - sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ - cd /opt/app-root/src/backend; \ - python manage.py loaddata wellsearch-codetables.json registries-codetables.json \ - '" - sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ - cd /opt/app-root/src/backend; \ - python manage.py loaddata wellsearch.json.gz registries.json \ - '" - sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ - cd /opt/app-root/src/backend; \ - python manage.py loaddata aquifers.json \ - '" - sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ - cd /opt/app-root/src/backend; \ - python manage.py createinitialrevisions \ - '" - isFixtured = true - }, - "Unit Tests: Python": { - unitTestsPythonQuick (context) - } - ) - } - } //end stage - }, //end branch - "Unit Tests" : { - /* Unit test stage - - use Django's manage.py to run python unit tests (w/ nose.cfg) - - use 'npm run unit' to run JavaScript unit tests - - stash test results for code quality stage - */ - _stage('Unit Tests', context) { - podTemplate( - label: "node-${context.uuid}", - name:"node-${context.uuid}", + stage('API Tests') { + steps { + script { + podTemplate( + label: "nodejs-${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}", + name: "nodejs-${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}", serviceAccount: 'jenkins', cloud: 'openshift', + activeDeadlineSeconds: 1800, containers: [ containerTemplate( name: 'jnlp', - image: 'jenkins/jnlp-slave:3.10-1-alpine', - args: '${computer.jnlpmac} ${computer.name}', - resourceRequestCpu: '100m', - resourceLimitCpu: '100m' - ), - containerTemplate( - name: 'app', - image: "docker-registry.default.svc:5000/moe-gwells-tools/gwells${context.buildNameSuffix}:${context.buildEnvName}", - ttyEnabled: true, - command: 'cat', - resourceRequestCpu: '2', - resourceLimitCpu: '2', - resourceRequestMemory: '2.5Gi', - resourceLimitMemory: '2.5Gi' - ) - ] - ) { - node("node-${context.uuid}") { - container('app') { - sh script: '''#!/usr/bin/container-entrypoint /bin/sh - printf "Python version: "&& python --version - printf "Pip version: "&& pip --version - printf "Node version: "&& node --version - printf "NPM version: "&& npm --version - ''' - - parallel ( - "Unit Tests: Python (w/ ZAP)": { - unitTestsPythonFull (context) - }, - "Unit Tests: Node": { - try { - sh script: '''#!/usr/bin/container-entrypoint /bin/sh - cd /opt/app-root/src/frontend - npm test - ''' - } finally { - if (isEnabled( context, 'Code Quality' )) { - sh script: '''#!/usr/bin/container-entrypoint /bin/sh - mkdir -p frontend/test/ - cp -R /opt/app-root/src/frontend/test/unit ./frontend/test/ - cp /opt/app-root/src/frontend/junit.xml ./frontend/ - ''' - archiveArtifacts allowEmptyArchive: true, artifacts: 'frontend/test/unit/**/*' - stash includes: 'frontend/test/unit/coverage/clover.xml', name: 'nodecoverage' - stash includes: 'frontend/junit.xml', name: 'nodejunit' - junit 'frontend/junit.xml' - publishHTML ( - target: [ - allowMissing: false, - alwaysLinkToLastBuild: false, - keepAll: true, - reportDir: 'frontend/test/unit/coverage/lcov-report/', - reportFiles: 'index.html', - reportName: "Node Coverage Report" - ] - ) - } - } - } //end branch - ) //end parallel - isUnitTested=true - } //end container - } //end node - } //end podTemplate - } //end stage - }, //end branch - "ZAP Security Scan": { - _stage('ZAP Security Scan', context) { - podTemplate( - label: "zap-${context.uuid}", - name: "zap-${context.uuid}", - serviceAccount: "jenkins", - cloud: "openshift", - containers: [ - containerTemplate( - name: 'jnlp', - image: 'docker-registry.default.svc:5000/moe-gwells-dev/owasp-zap-openshift', - resourceRequestCpu: '1', - resourceLimitCpu: '1', - resourceRequestMemory: '4Gi', - resourceLimitMemory: '4Gi', - workingDir: '/home/jenkins', + image: 'registry.access.redhat.com/openshift3/jenkins-agent-nodejs-8-rhel7', + resourceRequestCpu: '800m', + resourceLimitCpu: '800m', + resourceRequestMemory: '1Gi', + resourceLimitMemory: '1Gi', + workingDir: '/tmp', command: '', - args: '${computer.jnlpmac} ${computer.name}' + args: '${computer.jnlpmac} ${computer.name}', + envVars: [ + secretEnvVar( + key: 'GWELLS_API_TEST_USER', + secretName: 'apitest-secrets', + secretKey: 'username' + ), + secretEnvVar( + key: 'GWELLS_API_TEST_PASSWORD', + secretName: 'apitest-secrets', + secretKey: 'password' + ), + secretEnvVar( + key: 'GWELLS_API_TEST_AUTH_SERVER', + secretName: 'apitest-secrets', + secretKey: 'auth_server' + ), + secretEnvVar( + key: 'GWELLS_API_TEST_CLIENT_ID', + secretName: 'apitest-secrets', + secretKey: 'client_id' + ), + secretEnvVar( + key: 'GWELLS_API_TEST_CLIENT_SECRET', + secretName: 'apitest-secrets', + secretKey: 'client_secret' + ) + ] ) ] ) { - node("zap-${context.uuid}") { - //the checkout is mandatory - echo "checking out source" - echo "Build: ${BUILD_ID}" + node("nodejs-${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}") { checkout scm - dir('zap') { - waitUntil { - sleep 5 - return isDeployed - } - def retVal = sh ( - script: """ - set -eux - ./runzap.sh - """ - ) - publishHTML( - target: [ - allowMissing: false, - alwaysLinkToLastBuild: false, - keepAll: true, - reportDir: '/zap/wrk', - reportFiles: 'index.html', - reportName: 'ZAP Full Scan', - reportTitles: 'ZAP Full Scan' - ] - ) - echo "Return value is: ${retVal}" - } - } //end node - } //end podTemplate - } //end stage - }, //end branch - "Functional Tests":{ - waitUntil { - sleep 5 - return isDeployed - } - _stage('Functional Tests', context){ - String baseURL = context.deployments['dev'].environmentUrl.substring( - 0, - context.deployments['dev'].environmentUrl.indexOf('/', 8) + 1 - ) - podTemplate( - label: "bddstack-${context.uuid}", - name: "bddstack-${context.uuid}", - serviceAccount: 'jenkins', - cloud: 'openshift', - containers: [ - containerTemplate( - name: 'jnlp', - image: 'docker-registry.default.svc:5000/openshift/jenkins-slave-bddstack', - resourceRequestCpu: '800m', - resourceLimitCpu: '800m', - resourceRequestMemory: '3Gi', - resourceLimitMemory: '3Gi', - workingDir: '/home/jenkins', - command: '', - args: '${computer.jnlpmac} ${computer.name}', - envVars: [ - envVar(key:'BASEURL', value: baseURL), - envVar(key:'GRADLE_USER_HOME', value: '/var/cache/artifacts/gradle') - ] - ) - ], - volumes: [ - persistentVolumeClaim( - mountPath: '/var/cache/artifacts', - claimName: 'cache', - readOnly: false - ) - ] - ){ - node("bddstack-${context.uuid}") { - echo "Build: ${BUILD_ID}" - echo "baseURL: ${baseURL}" - checkout scm - dir('functional-tests') { - waitUntil { - sleep 5 - return isFixtured - } - String gradleExitCode = "0" + dir('api-tests') { + sh 'npm install -g newman' + String BASEURL = "https://${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}.pathfinder.gov.bc.ca/gwells" try { - gradleExitCode = sh([ - script: "./gradlew chromeHeadlessTest", - returnStdout: true - ]).trim() + sh """ + newman run ./registries_api_tests.json \ + --global-var test_user=\$GWELLS_API_TEST_USER \ + --global-var test_password=\$GWELLS_API_TEST_PASSWORD \ + --global-var base_url=${BASEURL} \ + --global-var auth_server=\$GWELLS_API_TEST_AUTH_SERVER \ + --global-var client_id=\$GWELLS_API_TEST_CLIENT_ID \ + --global-var client_secret=\$GWELLS_API_TEST_CLIENT_SECRET \ + -r cli,junit,html + newman run ./wells_api_tests.json \ + --global-var test_user=\$GWELLS_API_TEST_USER \ + --global-var test_password=\$GWELLS_API_TEST_PASSWORD \ + --global-var base_url=${BASEURL} \ + --global-var auth_server=\$GWELLS_API_TEST_AUTH_SERVER \ + --global-var client_id=\$GWELLS_API_TEST_CLIENT_ID \ + --global-var client_secret=\$GWELLS_API_TEST_CLIENT_SECRET \ + -r cli,junit,html + newman run ./submissions_api_tests.json \ + --global-var test_user=\$GWELLS_API_TEST_USER \ + --global-var test_password=\$GWELLS_API_TEST_PASSWORD \ + --global-var base_url=${BASEURL} \ + --global-var auth_server=\$GWELLS_API_TEST_AUTH_SERVER \ + --global-var client_id=\$GWELLS_API_TEST_CLIENT_ID \ + --global-var client_secret=\$GWELLS_API_TEST_CLIENT_SECRET \ + -r cli,junit,html + newman run ./aquifers_api_tests.json \ + --global-var test_user=\$GWELLS_API_TEST_USER \ + --global-var test_password=\$GWELLS_API_TEST_PASSWORD \ + --global-var base_url=${BASEURL} \ + --global-var auth_server=\$GWELLS_API_TEST_AUTH_SERVER \ + --global-var client_id=\$GWELLS_API_TEST_CLIENT_ID \ + --global-var client_secret=\$GWELLS_API_TEST_CLIENT_SECRET \ + -r cli,junit,html + """ } finally { - echo "gradleExitCode: ${gradleExitCode}" - if (gradleExitCode != "0" ) { - archiveArtifacts allowEmptyArchive: true, artifacts: 'build/reports/geb/**/*' - junit testResults:'build/test-results/**/*.xml', allowEmptyResults:true - publishHTML ( - target: [ - allowMissing: true, - alwaysLinkToLastBuild: false, - keepAll: true, - reportDir: 'build/reports/spock', - reportFiles: 'index.html', - reportName: "Test: BDD Spock Report" - ] - ) - publishHTML ( - target: [ - allowMissing: true, - alwaysLinkToLastBuild: false, - keepAll: true, - reportDir: 'build/reports/tests/chromeHeadlessTest', - reportFiles: 'index.html', - reportName: "Test: Full Test Report" - ] - ) - } + junit 'newman/*.xml' + publishHTML ( + target: [ + allowMissing: false, + alwaysLinkToLastBuild: false, + keepAll: true, + reportDir: 'newman', + reportFiles: 'newman*.html', + reportName: "API Test Report" + ] + ) + stash includes: 'newman/*.xml', name: 'api-tests' } - } //end dir - } //end node - } //end podTemplate - } //end stage - }, //end branch - "API Tests": { - _stage('API Tests', context) { - waitUntil { - sleep 5 - return isDeployed + } + } } - podTemplate( - label: "nodejs-${context.uuid}", - name: "nodejs-${context.uuid}", + } + } + } + + // the Promote to Test stage allows approving the tagging of the newly built image into the test environment, + // which will trigger an automatic deployment of that image. + // The deployment configs in the openshift folder are applied first in case there are any changes to the templates. + // this stage should only occur when the pull request is being made against the master branch. + stage('Deploy image to staging') { + when { + expression { env.CHANGE_TARGET == 'master' } + } + steps { + script { + openshift.withCluster() { + openshift.withProject(TEST_PROJECT) { + input "Deploy to staging?" + + echo "Preparing..." + + // Process db and app template into list objects + // - variable substitution + echo "Processing build templates" + def dbtemplate = openshift.process("-f", + "openshift/postgresql.bc.json", + "ENV_NAME=${TEST_SUFFIX}" + ) + openshift.apply(dbtemplate) + + echo "Updating staging deployment..." + + def deployDBTemplate = openshift.process("-f", + "openshift/postgresql.dc.json", + "NAME_SUFFIX=-${TEST_SUFFIX}", + "DATABASE_SERVICE_NAME=gwells-pgsql-${TEST_SUFFIX}", + "IMAGE_STREAM_NAMESPACE=''", + "IMAGE_STREAM_NAME=gwells-postgresql-${TEST_SUFFIX}", + "IMAGE_STREAM_VERSION=${TEST_SUFFIX}", + "POSTGRESQL_DATABASE=gwells", + "VOLUME_CAPACITY=5Gi" + ) + + def deployTemplate = openshift.process("-f", + "openshift/backend.dc.json", + "NAME_SUFFIX=-${TEST_SUFFIX}", + "ENV_NAME=${TEST_SUFFIX}", + "HOST=${APP_NAME}-${TEST_SUFFIX}.pathfinder.gov.bc.ca", + ) + + // some objects need to be copied from a base secret or configmap + // these objects have an annotation "as-copy-of" in their object spec (e.g. an object in backend.dc.json) + echo "Creating configmaps and secrets objects" + List newObjectCopies = [] + + // todo: refactor to explicitly copy the objects we need + for (o in (deployTemplate + deployDBTemplate)) { + + // only perform this operation on objects with 'as-copy-of' + def sourceName = o.metadata && o.metadata.annotations && o.metadata.annotations['as-copy-of'] + if (sourceName && sourceName.length() > 0) { + def selector = openshift.selector("${o.kind}/${sourceName}") + if (selector.count() == 1) { + + // create a copy of the object and add it to the new list of objects to be applied + Map copiedModel = selector.object(exportable:true) + copiedModel.metadata.name = o.metadata.name + echo "Copying ${o.kind} ${o.metadata.name}" + newObjectCopies.add(copiedModel) + } + } + } + + // apply the templates, which will create new objects or modify existing ones as necessary. + // the copies of base objects (secrets, configmaps) are also applied. + echo "Applying deployment config for pull request ${PR_NUM} on ${TEST_PROJECT}" + + openshift.apply(deployTemplate).label(['app':"gwells-${TEST_SUFFIX}", 'app-name':"${APP_NAME}", 'env-name':"${TEST_SUFFIX}"], "--overwrite") + openshift.apply(deployDBTemplate).label(['app':"gwells-${TEST_SUFFIX}", 'app-name':"${APP_NAME}", 'env-name':"${TEST_SUFFIX}"], "--overwrite") + openshift.apply(newObjectCopies).label(['app':"gwells-${TEST_SUFFIX}", 'app-name':"${APP_NAME}", 'env-name':"${TEST_SUFFIX}"], "--overwrite") + echo "Successfully applied TEST deployment config" + + // promote the newly built image to DEV + echo "Tagging new image to TEST imagestream." + + // Application/database images are tagged in the tools imagestream as the new test/prod image + openshift.tag("${TOOLS_PROJECT}/gwells-application:${PR_NUM}", "${TOOLS_PROJECT}/gwells-application:${TEST_SUFFIX}") // todo: clean up labels/tags + // openshift.tag("${TOOLS_PROJECT}/gwells-postgresql:test", "${TOOLS_PROJECT}/gwells-postgresql:${TEST_SUFFIX}") + + // Images are then tagged into the target environment namespace (test or prod) + openshift.tag("${TOOLS_PROJECT}/gwells-application:${TEST_SUFFIX}", "${TEST_PROJECT}/gwells-${TEST_SUFFIX}:${TEST_SUFFIX}") // todo: clean up labels/tags + openshift.tag("${TOOLS_PROJECT}/gwells-postgresql:test", "${TEST_PROJECT}/gwells-postgresql-${TEST_SUFFIX}:${TEST_SUFFIX}") // todo: clean up labels/tags + + def targetTestURL = "https://${APP_NAME}-${TEST_SUFFIX}.pathfinder.gov.bc.ca/gwells" + def ghDeploymentId = new GitHubHelper().createDeployment(this, "pull/${env.CHANGE_ID}/head", ['environment':"${TEST_SUFFIX}", 'task':"deploy:pull:${env.CHANGE_ID}"]) + new GitHubHelper().createDeploymentStatus(this, ghDeploymentId, 'PENDING', ['targetUrl':"${targetTestURL}"]) + + // monitor the deployment status and wait until deployment is successful + echo "Waiting for deployment to TEST..." + def newVersion = openshift.selector("dc", "gwells-${TEST_SUFFIX}").object().status.latestVersion + def pods = openshift.selector('pod', [deployment: "gwells-${TEST_SUFFIX}-${newVersion}"]) + + // wait until at least one pod reports as ready + timeout(15) { + pods.untilEach(2) { + return it.object().status.containerStatuses.every { + it.ready + } + } + } + + new GitHubHelper().createDeploymentStatus(this, ghDeploymentId, 'SUCCESS', ['targetUrl':"${targetTestURL}"]) + + echo "TEST deployment successful." + } + } + } + } + } + + stage('API Tests against Staging') { + when { + expression { env.CHANGE_TARGET == 'master' } + } + steps { + script { + podTemplate( + label: "nodejs-${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}-${env.CHANGE_ID}", + name: "nodejs-${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}-${env.CHANGE_ID}", serviceAccount: 'jenkins', cloud: 'openshift', + activeDeadlineSeconds: 1800, containers: [ containerTemplate( name: 'jnlp', @@ -601,16 +494,11 @@ parallel ( ) ] ) { - node("nodejs-${context.uuid}") { + node("nodejs-${APP_NAME}-${DEV_SUFFIX}-${PR_NUM}-${env.CHANGE_ID}") { checkout scm dir('api-tests') { sh 'npm install -g newman' - waitUntil { - sleep 5 - return isFixtured - } - String BASEURL = context.deployments['dev'].environmentUrl.substring(0, context.deployments['dev'].environmentUrl.indexOf('/', 8) + 1) - BASEURL += "gwells" + String BASEURL = "https://gwells-${TEST_SUFFIX}.pathfinder.gov.bc.ca/gwells" try { sh """ newman run ./registries_api_tests.json \ @@ -647,263 +535,146 @@ parallel ( -r cli,junit,html """ } finally { - junit 'newman/*.xml' - publishHTML ( - target: [ - allowMissing: false, - alwaysLinkToLastBuild: false, - keepAll: true, - reportDir: 'newman', - reportFiles: 'newman*.html', - reportName: "API Test Report" - ] - ) - stash includes: 'newman/*.xml', name: 'api-tests' + junit 'newman/*.xml' + publishHTML ( + target: [ + allowMissing: false, + alwaysLinkToLastBuild: false, + keepAll: true, + reportDir: 'newman', + reportFiles: 'newman*.html', + reportName: "API Test Report" + ] + ) + stash includes: 'newman/*.xml', name: 'api-tests' } - } // end dir - } //end node - } //end podTemplate - } //end stage - }, //end branch - "Code Quality": { - /* Code quality stage - pipeline step/closure - - unstash unit test results (previous stage) - - use SonarQube to consume results (*.xml) - */ - _stage('Code Quality', context) { - podTemplate( - name: "sonar-runner${context.uuid}", - label: "sonar-runner${context.uuid}", - serviceAccount: 'jenkins', - cloud: 'openshift', - containers:[ - containerTemplate( - name: 'jnlp', - resourceRequestMemory: '4Gi', - resourceLimitMemory: '4Gi', - resourceRequestCpu: '4000m', - resourceLimitCpu: '4000m', - image: 'registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7:v3.7', - workingDir: '/tmp', - args: '${computer.jnlpmac} ${computer.name}', - envVars: [ - envVar(key:'GRADLE_USER_HOME', value: '/var/cache/artifacts/gradle') - ] - ) - ], - volumes: [ - persistentVolumeClaim( - mountPath: '/var/cache/artifacts', - claimName: 'cache', - readOnly: false - ) - ] - ){ - node("sonar-runner${context.uuid}") { - //the checkout is mandatory, otherwise code quality check would fail - echo "checking out source" - echo "Build: ${BUILD_ID}" - checkout scm - - String SONARQUBE_URL = 'https://sonarqube-moe-gwells-tools.pathfinder.gov.bc.ca' - echo "SONARQUBE_URL: ${SONARQUBE_URL}" - waitUntil { - sleep 5 - return isUnitTested - } - dir('app') { - unstash 'nodejunit' - unstash 'nodecoverage' } - dir('sonar-runner') { - unstash 'coverage' - sh script: - """ - ./gradlew -q dependencies - ./gradlew sonarqube -Dsonar.host.url=${SONARQUBE_URL} -Dsonar.verbose=true \ - --stacktrace --info -Dsonar.sources=.. - """, - returnStdout: true - } - } //end node - } //end podTemplate - } //end stage - } //end branch -) //end parallel - - -/* Continuous Deployment (CD) - For PRs to the master branch, reserved for release branches and hotfixes - Iterates through DEV (skipped), TEST and PROD environments - - [prompt/stop] - || deployment to persistent TEST environment (sets isDeployed=true) - || smoke tests (executes on isDeployed) - - [prompt/stop] - - deployment to persistent PROD environment - - GitHub tasks (merge, close PR, deleteproduction branch) -*/ -for(String envKeyName: context.env.keySet() as String[]){ - String stageDeployName=envKeyName.toUpperCase() - - if (!"DEV".equalsIgnoreCase(stageDeployName) && "master".equalsIgnoreCase(env.CHANGE_TARGET)) { - _stage("Approve - ${stageDeployName}", context) { - node('master') { - new OpenShiftHelper().waitUntilEnvironmentIsReady(this, context, envKeyName) - } - def inputResponse = null; - try{ - inputResponse = input( - id: "deploy_${stageDeployName.toLowerCase()}", - message: "Deploy to ${stageDeployName}?", - ok: 'Approve', - submitterParameter: 'approved_by' - ) - }catch(ex){ - error "Pipeline has been aborted. - ${ex}" + } } - GitHubHelper.getPullRequest(this).comment( - "User '${inputResponse}' has approved deployment to '${stageDeployName}'" - ) } + } + } - isDeployed = false - parallel ( - "Deploy - ${stageDeployName}": { - _stage("Deploy - ${stageDeployName}", context) { - node('master') { - new OpenShiftHelper().deploy(this, context, envKeyName) - isDeployed = true - } + + stage('Deploy image to Production') { + when { + expression { env.CHANGE_TARGET == 'master' } + } + steps { + script { + openshift.withCluster() { + openshift.withProject(PROD_PROJECT) { + input "Deploy to production?" + + echo "Updating production deployment..." + def deployTemplate = openshift.process("-f", + "openshift/backend.dc.json", + "NAME_SUFFIX=-${PROD_SUFFIX}", + "ENV_NAME=${PROD_SUFFIX}", + "HOST=${APP_NAME}-${PROD_SUFFIX}.pathfinder.gov.bc.ca", + ) + + def deployDBTemplate = openshift.process("-f", + "openshift/postgresql.dc.json", + "NAME_SUFFIX=-${PROD_SUFFIX}", + "DATABASE_SERVICE_NAME=gwells-pgsql-${PROD_SUFFIX}", + "IMAGE_STREAM_NAMESPACE=''", + "IMAGE_STREAM_NAME=gwells-postgresql-${PROD_SUFFIX}", + "IMAGE_STREAM_VERSION=${PROD_SUFFIX}", + "POSTGRESQL_DATABASE=gwells", + "VOLUME_CAPACITY=20Gi" + ) + + // some objects need to be copied from a base secret or configmap + // these objects have an annotation "as-copy-of" in their object spec (e.g. an object in backend.dc.json) + echo "Creating configmaps and secrets objects" + List newObjectCopies = [] + + for (o in (deployTemplate + deployDBTemplate)) { + + // only perform this operation on objects with 'as-copy-of' + def sourceName = o.metadata && o.metadata.annotations && o.metadata.annotations['as-copy-of'] + if (sourceName && sourceName.length() > 0) { + def selector = openshift.selector("${o.kind}/${sourceName}") + if (selector.count() == 1) { + + // create a copy of the object and add it to the new list of objects to be applied + Map copiedModel = selector.object(exportable:true) + copiedModel.metadata.name = o.metadata.name + echo "Copying ${o.kind} ${o.metadata.name}" + newObjectCopies.add(copiedModel) + } } - }, - "Smoke Test - ${stageDeployName}": { - waitUntil { - sleep 5 - return isDeployed + } + + // apply the templates, which will create new objects or modify existing ones as necessary. + // the copies of base objects (secrets, configmaps) are also applied. + echo "Applying deployment config for pull request ${PR_NUM} on ${PROD_PROJECT}" + + openshift.apply(deployTemplate).label(['app':"gwells-${PROD_SUFFIX}", 'app-name':"${APP_NAME}", 'env-name':"${PROD_SUFFIX}"], "--overwrite") + openshift.apply(deployDBTemplate).label(['app':"gwells-${PROD_SUFFIX}", 'app-name':"${APP_NAME}", 'env-name':"${PROD_SUFFIX}"], "--overwrite") + openshift.apply(newObjectCopies).label(['app':"gwells-${PROD_SUFFIX}", 'app-name':"${APP_NAME}", 'env-name':"${PROD_SUFFIX}"], "--overwrite") + echo "Successfully applied production deployment config" + + // promote the newly built image to DEV + echo "Tagging new image to production imagestream." + + // Application/database images are tagged in the tools imagestream as the new prod image + openshift.tag("${TOOLS_PROJECT}/gwells-application:${PR_NUM}", "${TOOLS_PROJECT}/gwells-application:${PROD_SUFFIX}") // todo: clean up labels/tags + + // TODO: determine best way to manage database images (at the moment they never change, but we don't want an unforeseen change to impact prod) + // openshift.tag("${TOOLS_PROJECT}/gwells-postgresql:prod", "${TOOLS_PROJECT}/gwells-postgresql:${PROD_SUFFIX}") + + // Images are then tagged into the target environment namespace (prod) + openshift.tag("${TOOLS_PROJECT}/gwells-application:${PROD_SUFFIX}", "${PROD_PROJECT}/gwells-${PROD_SUFFIX}:${PROD_SUFFIX}") // todo: clean up labels/tags + openshift.tag("${TOOLS_PROJECT}/gwells-postgresql:prod", "${PROD_PROJECT}/gwells-postgresql-${PROD_SUFFIX}:${PROD_SUFFIX}") // todo: clean up labels/tags + + def targetProdURL = "https://apps.nrs.gov.bc.ca/gwells/" + def ghDeploymentId = new GitHubHelper().createDeployment(this, "pull/${env.CHANGE_ID}/head", ['environment':"${PROD_SUFFIX}", 'task':"deploy:pull:${env.CHANGE_ID}"]) + new GitHubHelper().createDeploymentStatus(this, ghDeploymentId, 'PENDING', ['targetUrl':"${targetProdURL}"]) + + // monitor the deployment status and wait until pgsql-${ deployment is successful + echo "Waiting for deployment to production..." + def newVersion = openshift.selector("dc", "gwells-${PROD_SUFFIX}").object().status.latestVersion + def pods = openshift.selector('pod', [deployment: "gwells-${PROD_SUFFIX}-${newVersion}"]) + + // wait until pods reports as ready + timeout(15) { + pods.untilEach(2) { + return it.object().status.containerStatuses.every { + it.ready + } } - _stage("Smoke Test - ${stageDeployName}", context){ - String baseURL = context.deployments[envKeyName].environmentUrl.substring( - 0, - context.deployments[envKeyName].environmentUrl.indexOf('/', 8) + 1 - ) - podTemplate( - label: "bddstack-${context.uuid}", - name: "bddstack-${context.uuid}", - serviceAccount: 'jenkins', - cloud: 'openshift', - containers: [ - containerTemplate( - name: 'jnlp', - image: 'docker-registry.default.svc:5000/openshift/jenkins-slave-bddstack', - resourceRequestCpu: '800m', - resourceLimitCpu: '800m', - resourceRequestMemory: '3Gi', - resourceLimitMemory: '3Gi', - workingDir: '/home/jenkins', - command: '', - args: '${computer.jnlpmac} ${computer.name}', - envVars: [ - envVar(key:'BASEURL', value: baseURL), - envVar(key:'GRADLE_USER_HOME', value: '/var/cache/artifacts/gradle') - ] - ) - ], - volumes: [ - persistentVolumeClaim( - mountPath: '/var/cache/artifacts', - claimName: 'cache', - readOnly: false - ) - ] - ){ - node("bddstack-${context.uuid}") { - echo "Build: ${BUILD_ID}" - echo "baseURL: ${baseURL}" - checkout scm - dir('functional-tests') { - try { - sh './gradlew -DchromeHeadlessTest.single=WellDetails chromeHeadlessTest' - } finally { - archiveArtifacts allowEmptyArchive: true, artifacts: 'build/reports/geb/**/*' - junit testResults:'build/test-results/**/*.xml', allowEmptyResults:true - publishHTML ( - target: [ - allowMissing: true, - alwaysLinkToLastBuild: false, - keepAll: true, - reportDir: 'build/reports/spock', - reportFiles: 'index.html', - reportName: "Test: BDD Spock Report" - ] - ) - publishHTML ( - target: [ - allowMissing: true, - alwaysLinkToLastBuild: false, - keepAll: true, - reportDir: 'build/reports/tests/chromeHeadlessTest', - reportFiles: 'index.html', - reportName: "Test: Full Test Report" - ] - ) - } - } //end dir - } //end node - } //end podTemplate - } //end stage - } - ) - } //end if -} // end for - - -/* Cleanup stage - pipeline step/closure - - Prompt user to continue - - Remove temporary OpenShift resources (moe-gwells-dev) - - Merge and delete branches -*/ -stage('Cleanup') { - - def inputResponse = null - String mergeMethod='merge' - - waitUntil { - boolean isDone=false - try{ - inputResponse=input( - id: 'close_pr', - message: "Ready to Accept/Merge (using '${mergeMethod}' method), and Close pull-request #${env.CHANGE_ID}?", - ok: 'Yes', - submitter: 'authenticated', - submitterParameter: 'approver' - ) - echo "inputResponse:${inputResponse}" - - echo "Merging and Closing PR" - GitHubHelper.mergeAndClosePullRequest(this, mergeMethod) - - echo "Clearing OpenShift resources" - new OpenShiftHelper().cleanup(this, context) - - isDone=true - }catch (ex){ - echo "${stackTraceAsString(ex)}" - def inputAction = input( - message: "This 'Cleanup' stage has failed. See error above.", - ok: 'Confirm', - submitter: 'authenticated', - parameters: [ - choice( - name: 'action', - choices: 'Re-run\nIgnore', - description: 'What would you like to do?' - ) - ] - ) - if ('Ignore'.equalsIgnoreCase(inputAction)){ - isDone=true + } + + echo "Production deployment successful." + // slack & github notifications that a new deployment is ready + + new GitHubHelper().createDeploymentStatus(this, ghDeploymentId, 'SUCCESS', ['targetUrl':"${targetProdURL}"]) + + openshift.withProject(TOOLS_PROJECT) { + + // get a slack token + def token = openshift.selector("secret", "slack").object().data.token.decodeBase64() + token = new String(token) + + // build a message to send to the channel + def message = [:] + message.channel = "#gwells" + message.text = "A new production deployment was rolled out at https://apps.nrs.gov.bc.ca/gwells/" + payload = JsonOutput.toJson(message) + + // Approve script here: https://jenkins-moe-gwells-tools.pathfinder.gov.bc.ca/scriptApproval/ + sh ( + script: """curl -X POST -H "Content-Type: application/json" --data \'${payload}\' https://devopspathfinder.slack.com/services/hooks/jenkins-ci?token=${token}""", + returnStdout: true + ).trim() + + } } + } } - return isDone - } //end waitUntil + } + } + } } diff --git a/Jenkinsfile.backup b/Jenkinsfile.backup new file mode 100644 index 0000000000..0d92e57f1e --- /dev/null +++ b/Jenkinsfile.backup @@ -0,0 +1,909 @@ +// Jenkinsfile (Scripted Pipeline) + +/* Gotchas: + - PodTemplate name/label has to be unique to ensure proper caching/validation + - https://gist.github.com/matthiasbalke/3c9ecccbea1d460ee4c3fbc5843ede4a + + Libraries: + - https://github.com/BCDevOps/jenkins-pipeline-shared-lib + - http://github-api.kohsuke.org/apidocs/index.html +*/ +import hudson.model.Result; +import jenkins.model.CauseOfInterruption.UserInterruption; +import org.kohsuke.github.* +import bcgov.OpenShiftHelper +import bcgov.GitHubHelper + + +// Print stack trace of error +@NonCPS +private static String stackTraceAsString(Throwable t) { + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + return sw.toString() +} + + +// Notify stage status and pass to Jenkins-GitHub library +void notifyStageStatus (Map context, String name, String status) { + GitHubHelper.createCommitStatus( + this, + context.pullRequest.head, + status, + "${env.BUILD_URL}", + "Stage '${name}'", + "stages/${name.toLowerCase()}" + ) +} + + +// Check if a stage is enabled (true|false in context) +boolean isEnabled (Map context, String stageName) { + def stageOpt =(context?.stages?:[:])[stageName] + return (stageOpt == null || stageOpt == true) +} + + +// Python tests can run early an in the container if Code Quality is disabled +void unitTestsPython (Map context, boolean isQuick=false) { + boolean doCodeQuality = isEnabled( context, 'Code Quality' ) + if (doCodeQuality && !isQuick) { + try { + echo "Running tests with artifact stash for SonarQube" + sh script: '''#!/usr/bin/container-entrypoint /bin/sh + cd /opt/app-root/src/backend + DATABASE_ENGINE=sqlite DEBUG=False TEMPLATE_DEBUG=False python manage.py test -c nose.cfg + ''' + sh script: '''#!/usr/bin/container-entrypoint /bin/sh + cp /opt/app-root/src/backend/nosetests.xml ./ + cp /opt/app-root/src/backend/coverage.xml ./ + ''' + stash includes: 'nosetests.xml,coverage.xml', name: 'coverage' + } finally { + stash includes: 'nosetests.xml,coverage.xml', name: 'coverage' + junit 'nosetests.xml' + } + } else if (!doCodeQuality && isQuick){ + echo "Running short tests w/o artifact stash since SonarQube is disabled" + String deploymentConfigName = "gwells${context.deployments['dev'].dcSuffix}" + String projectName = context.deployments['dev'].projectName + String podName = openshift.withProject(projectName){ + return openshift.selector('pod', ['deploymentconfig':deploymentConfigName]).objects()[1].metadata.name + } + sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ + cd /opt/app-root/src/backend; \ + DATABASE_ENGINE=sqlite DEBUG=False TEMPLATE_DEBUG=False python manage.py test -c nose.cfg \ + '" + } else { + echo "Python unit tests are being skipped at this stage" + echo "doCodeQuality = ${doCodeQuality}" + echo "isQuick = ${isQuick}" + } +} +void unitTestsPythonQuick (Map context) {unitTestsPython (context, true)} +void unitTestsPythonFull (Map context) {unitTestsPython (context, false)} + + +/* _Stage wrapper: + - runs stages against true|false in map context + - receives stages defined separately in closures (body) + - catches errors and provides output +*/ +def _stage(String name, Map context, boolean retry=0, boolean withCommitStatus=true, Closure body) { + timestamps { + echo "Running Stage '${name}'" + if (isEnabled(context,name)){ + stage(name) { + waitUntil { + notifyStageStatus(context, name, 'PENDING') + boolean isDone=false + try{ + body() + isDone=true + notifyStageStatus(context, name, 'SUCCESS') + echo "Completed Stage '${name}'" + }catch (ex){ + notifyStageStatus(context, name, 'FAILURE') + echo "${stackTraceAsString(ex)}" + def inputAction = input( + message: "This step (${name}) has failed. See error above.", + ok: 'Confirm', + parameters: [ + choice( + name: 'action', + choices: 'Re-run\nIgnore', + description: 'What would you like to do?' + ) + ] + ) + if ('Ignore'.equalsIgnoreCase(inputAction)){ + isDone=true + } + } + return isDone + } //end waitUntil + } //end Stage + }else{ + stage(name) { + echo 'Skipping' + } + } + } +} + + +/* Project and build settings + Includes: + - build (*.bc) and config templates (*.dc) + - stage names and enabled status (true|false) +*/ +Map context = [ + 'name': 'gwells', + 'uuid' : "${env.JOB_BASE_NAME}-${env.BUILD_NUMBER}-${env.CHANGE_ID}", + 'env': [ + 'dev':[:], + 'test':[ + 'params':[ + 'host':'gwells-test.pathfinder.gov.bc.ca', + 'DB_PVC_SIZE':'5Gi' + ] + ], + 'prod':[ + 'params':[ + 'host':'gwells-prod.pathfinder.gov.bc.ca', + 'DB_PVC_SIZE':'5Gi' + ] + ] + ], + 'templates': [ + 'build':[ + ['file':'openshift/postgresql.bc.json'], + ['file':'openshift/backend.bc.json'] + ], + 'deployment':[ + [ + 'file':'openshift/postgresql.dc.json', + 'params':[ + 'DATABASE_SERVICE_NAME':'gwells-pgsql${deploy.dcSuffix}', + 'IMAGE_STREAM_NAMESPACE':'', + 'IMAGE_STREAM_NAME':'gwells-postgresql${deploy.dcSuffix}', + 'IMAGE_STREAM_VERSION':'${deploy.envName}', + 'POSTGRESQL_DATABASE':'gwells', + 'VOLUME_CAPACITY':'${env[DEPLOY_ENV_NAME]?.params?.DB_PVC_SIZE?:"1Gi"}' + ] + ], + [ + 'file':'openshift/backend.dc.json', + 'params':[ + 'HOST':'${env[DEPLOY_ENV_NAME]?.params?.host?:("gwells" + deployments[DEPLOY_ENV_NAME].dcSuffix + "-" + deployments[DEPLOY_ENV_NAME].projectName + ".pathfinder.gov.bc.ca")}' + ] + ] + ] + ], + stages:[ + 'Load Fixtures': true, + 'API Tests': true, + 'Functional Tests': true, + 'Unit Tests': true, + 'Code Quality': false, + 'ZAP Security Scan': false + ], + pullRequest:[ + 'id': env.CHANGE_ID, + 'head': GitHubHelper.getPullRequestLastCommitId(this) + ] +] + + +/* Jenkins properties can be set on a pipeline-by-pipeline basis + See Jenkins' Pipeline Systax for generation + Globally equivalent to Jenkins > Manage Jenkins > Configure System + https://jenkins.io/doc/pipeline/steps/workflow-multibranch/#properties-set-job-properties +*/ +properties([ + buildDiscarder( + logRotator( + artifactDaysToKeepStr: '', + artifactNumToKeepStr: '', + daysToKeepStr: '', + numToKeepStr: '5' + ) + ), + durabilityHint( + 'PERFORMANCE_OPTIMIZED' + ), + disableResume() +]) + + +/* Prepare stage + - abort any existing builds + - echo pull request number +*/ +stage('Prepare') { + abortAllPreviousBuildInProgress(currentBuild) +} + + +/* Build stage + - applying OpenShift build configs + - creating OpenShift imagestreams, annotations and builds + - build time optimizations (e.g. image reuse, build scheduling/readiness) +*/ +_stage('Build', context) { + node('master') { + checkout scm + new OpenShiftHelper().build(this, context) + if ("master".equalsIgnoreCase(env.CHANGE_TARGET)) { + new OpenShiftHelper().prepareForCD(this, context) + new OpenShiftHelper().waitUntilEnvironmentIsReady(this, context, 'dev') + } + deleteDir() + } +} //end stage + + +/* Continuous Integration (CI) + For feature branches merging into a release branch + || Deploy and Load Fixtures (sets isDeployed and isFixtured=true) + || Unit tests (sets isUnitTested=true) + -> || Python tests + || Node tests + || ZAP Security Scan (executes on isDeployed) + || Functional tests (executes on isFixtured) + || API tests (executes on isFixtured) + || Code quality (executes on isUnitTested) +*/ +boolean isDeployed = false +boolean isFixtured = false +boolean isUnitTested = false +parallel ( + "Deploy and Load Fixtures" : { + + _stage('Deploy', context) { + node('master') { + new OpenShiftHelper().deploy(this, context, 'dev') + String deploymentConfigName = "gwells${context.deployments['dev'].dcSuffix}" + String projectName = context.deployments['dev'].projectName + openshift.withProject(projectName) { + // get list of pods for the new deployment + def latestDeployment = openshift.selector('dc', deploymentConfigName).object().status.latestVersion + def pods = openshift.selector('pod', [deployment: "${deploymentConfigName}-${latestDeployment}"]) + + pods.untilEach(1) { + return it.object().status.containerStatuses.every { + it.ready + } + } + } + isDeployed = true + } + } + + _stage('Load Fixtures', context) { + node('master'){ + parallel ( + "Load Fixtures": { + String deploymentConfigName = "gwells${context.deployments['dev'].dcSuffix}" + String projectName = context.deployments['dev'].projectName + String podName = openshift.withProject(projectName){ + return openshift.selector('pod', ['deploymentconfig':deploymentConfigName]).objects()[0].metadata.name + } + + /* All of these commands could be run in one go, and be more performant, but then + it becomes difficult to see which on of the steps failed. Instead, each step is + executed by itself. */ + sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ + cd /opt/app-root/src/backend; \ + python manage.py migrate \ + '" + sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ + cd /opt/app-root/src/backend; \ + python manage.py loaddata gwells-codetables.json \ + '" + sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ + cd /opt/app-root/src/backend; \ + python manage.py loaddata wellsearch-codetables.json registries-codetables.json \ + '" + sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ + cd /opt/app-root/src/backend; \ + python manage.py loaddata wellsearch.json.gz registries.json \ + '" + sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ + cd /opt/app-root/src/backend; \ + python manage.py loaddata aquifers.json \ + '" + sh "oc exec '${podName}' -n '${projectName}' -- bash -c '\ + cd /opt/app-root/src/backend; \ + python manage.py createinitialrevisions \ + '" + isFixtured = true + }, + "Unit Tests: Python": { + unitTestsPythonQuick (context) + } + ) + } + } //end stage + }, //end branch + "Unit Tests" : { + /* Unit test stage + - use Django's manage.py to run python unit tests (w/ nose.cfg) + - use 'npm run unit' to run JavaScript unit tests + - stash test results for code quality stage + */ + _stage('Unit Tests', context) { + podTemplate( + label: "node-${context.uuid}", + name:"node-${context.uuid}", + serviceAccount: 'jenkins', + cloud: 'openshift', + containers: [ + containerTemplate( + name: 'jnlp', + image: 'jenkins/jnlp-slave:3.10-1-alpine', + args: '${computer.jnlpmac} ${computer.name}', + resourceRequestCpu: '100m', + resourceLimitCpu: '100m' + ), + containerTemplate( + name: 'app', + image: "docker-registry.default.svc:5000/moe-gwells-tools/gwells${context.buildNameSuffix}:${context.buildEnvName}", + ttyEnabled: true, + command: 'cat', + resourceRequestCpu: '2', + resourceLimitCpu: '2', + resourceRequestMemory: '2.5Gi', + resourceLimitMemory: '2.5Gi' + ) + ] + ) { + node("node-${context.uuid}") { + container('app') { + sh script: '''#!/usr/bin/container-entrypoint /bin/sh + printf "Python version: "&& python --version + printf "Pip version: "&& pip --version + printf "Node version: "&& node --version + printf "NPM version: "&& npm --version + ''' + + parallel ( + "Unit Tests: Python (w/ ZAP)": { + unitTestsPythonFull (context) + }, + "Unit Tests: Node": { + try { + sh script: '''#!/usr/bin/container-entrypoint /bin/sh + cd /opt/app-root/src/frontend + npm test + ''' + } finally { + if (isEnabled( context, 'Code Quality' )) { + sh script: '''#!/usr/bin/container-entrypoint /bin/sh + mkdir -p frontend/test/ + cp -R /opt/app-root/src/frontend/test/unit ./frontend/test/ + cp /opt/app-root/src/frontend/junit.xml ./frontend/ + ''' + archiveArtifacts allowEmptyArchive: true, artifacts: 'frontend/test/unit/**/*' + stash includes: 'frontend/test/unit/coverage/clover.xml', name: 'nodecoverage' + stash includes: 'frontend/junit.xml', name: 'nodejunit' + junit 'frontend/junit.xml' + publishHTML ( + target: [ + allowMissing: false, + alwaysLinkToLastBuild: false, + keepAll: true, + reportDir: 'frontend/test/unit/coverage/lcov-report/', + reportFiles: 'index.html', + reportName: "Node Coverage Report" + ] + ) + } + } + } //end branch + ) //end parallel + isUnitTested=true + } //end container + } //end node + } //end podTemplate + } //end stage + }, //end branch + "ZAP Security Scan": { + _stage('ZAP Security Scan', context) { + podTemplate( + label: "zap-${context.uuid}", + name: "zap-${context.uuid}", + serviceAccount: "jenkins", + cloud: "openshift", + containers: [ + containerTemplate( + name: 'jnlp', + image: 'docker-registry.default.svc:5000/moe-gwells-dev/owasp-zap-openshift', + resourceRequestCpu: '1', + resourceLimitCpu: '1', + resourceRequestMemory: '4Gi', + resourceLimitMemory: '4Gi', + workingDir: '/home/jenkins', + command: '', + args: '${computer.jnlpmac} ${computer.name}' + ) + ] + ) { + node("zap-${context.uuid}") { + //the checkout is mandatory + echo "checking out source" + echo "Build: ${BUILD_ID}" + checkout scm + dir('zap') { + waitUntil { + sleep 5 + return isDeployed + } + def retVal = sh ( + script: """ + set -eux + ./runzap.sh + """ + ) + publishHTML( + target: [ + allowMissing: false, + alwaysLinkToLastBuild: false, + keepAll: true, + reportDir: '/zap/wrk', + reportFiles: 'index.html', + reportName: 'ZAP Full Scan', + reportTitles: 'ZAP Full Scan' + ] + ) + echo "Return value is: ${retVal}" + } + } //end node + } //end podTemplate + } //end stage + }, //end branch + "Functional Tests":{ + waitUntil { + sleep 5 + return isDeployed + } + _stage('Functional Tests', context){ + String baseURL = context.deployments['dev'].environmentUrl.substring( + 0, + context.deployments['dev'].environmentUrl.indexOf('/', 8) + 1 + ) + podTemplate( + label: "bddstack-${context.uuid}", + name: "bddstack-${context.uuid}", + serviceAccount: 'jenkins', + cloud: 'openshift', + containers: [ + containerTemplate( + name: 'jnlp', + image: 'docker-registry.default.svc:5000/openshift/jenkins-slave-bddstack', + resourceRequestCpu: '800m', + resourceLimitCpu: '800m', + resourceRequestMemory: '3Gi', + resourceLimitMemory: '3Gi', + workingDir: '/home/jenkins', + command: '', + args: '${computer.jnlpmac} ${computer.name}', + envVars: [ + envVar(key:'BASEURL', value: baseURL), + envVar(key:'GRADLE_USER_HOME', value: '/var/cache/artifacts/gradle') + ] + ) + ], + volumes: [ + persistentVolumeClaim( + mountPath: '/var/cache/artifacts', + claimName: 'cache', + readOnly: false + ) + ] + ){ + node("bddstack-${context.uuid}") { + echo "Build: ${BUILD_ID}" + echo "baseURL: ${baseURL}" + checkout scm + dir('functional-tests') { + waitUntil { + sleep 5 + return isFixtured + } + String gradleExitCode = "0" + try { + gradleExitCode = sh([ + script: "./gradlew chromeHeadlessTest", + returnStdout: true + ]).trim() + } finally { + echo "gradleExitCode: ${gradleExitCode}" + if (gradleExitCode != "0" ) { + archiveArtifacts allowEmptyArchive: true, artifacts: 'build/reports/geb/**/*' + junit testResults:'build/test-results/**/*.xml', allowEmptyResults:true + publishHTML ( + target: [ + allowMissing: true, + alwaysLinkToLastBuild: false, + keepAll: true, + reportDir: 'build/reports/spock', + reportFiles: 'index.html', + reportName: "Test: BDD Spock Report" + ] + ) + publishHTML ( + target: [ + allowMissing: true, + alwaysLinkToLastBuild: false, + keepAll: true, + reportDir: 'build/reports/tests/chromeHeadlessTest', + reportFiles: 'index.html', + reportName: "Test: Full Test Report" + ] + ) + } + } + } //end dir + } //end node + } //end podTemplate + } //end stage + }, //end branch + "API Tests": { + _stage('API Tests', context) { + waitUntil { + sleep 5 + return isDeployed + } + podTemplate( + label: "nodejs-${context.uuid}", + name: "nodejs-${context.uuid}", + serviceAccount: 'jenkins', + cloud: 'openshift', + containers: [ + containerTemplate( + name: 'jnlp', + image: 'registry.access.redhat.com/openshift3/jenkins-agent-nodejs-8-rhel7', + resourceRequestCpu: '800m', + resourceLimitCpu: '800m', + resourceRequestMemory: '1Gi', + resourceLimitMemory: '1Gi', + workingDir: '/tmp', + command: '', + args: '${computer.jnlpmac} ${computer.name}', + envVars: [ + secretEnvVar( + key: 'GWELLS_API_TEST_USER', + secretName: 'apitest-secrets', + secretKey: 'username' + ), + secretEnvVar( + key: 'GWELLS_API_TEST_PASSWORD', + secretName: 'apitest-secrets', + secretKey: 'password' + ), + secretEnvVar( + key: 'GWELLS_API_TEST_AUTH_SERVER', + secretName: 'apitest-secrets', + secretKey: 'auth_server' + ), + secretEnvVar( + key: 'GWELLS_API_TEST_CLIENT_ID', + secretName: 'apitest-secrets', + secretKey: 'client_id' + ), + secretEnvVar( + key: 'GWELLS_API_TEST_CLIENT_SECRET', + secretName: 'apitest-secrets', + secretKey: 'client_secret' + ) + ] + ) + ] + ) { + node("nodejs-${context.uuid}") { + checkout scm + dir('api-tests') { + sh 'npm install -g newman' + waitUntil { + sleep 5 + return isFixtured + } + String BASEURL = context.deployments['dev'].environmentUrl.substring(0, context.deployments['dev'].environmentUrl.indexOf('/', 8) + 1) + BASEURL += "gwells" + try { + sh """ + newman run ./registries_api_tests.json \ + --global-var test_user=\$GWELLS_API_TEST_USER \ + --global-var test_password=\$GWELLS_API_TEST_PASSWORD \ + --global-var base_url=${BASEURL} \ + --global-var auth_server=\$GWELLS_API_TEST_AUTH_SERVER \ + --global-var client_id=\$GWELLS_API_TEST_CLIENT_ID \ + --global-var client_secret=\$GWELLS_API_TEST_CLIENT_SECRET \ + -r cli,junit,html + newman run ./wells_api_tests.json \ + --global-var test_user=\$GWELLS_API_TEST_USER \ + --global-var test_password=\$GWELLS_API_TEST_PASSWORD \ + --global-var base_url=${BASEURL} \ + --global-var auth_server=\$GWELLS_API_TEST_AUTH_SERVER \ + --global-var client_id=\$GWELLS_API_TEST_CLIENT_ID \ + --global-var client_secret=\$GWELLS_API_TEST_CLIENT_SECRET \ + -r cli,junit,html + newman run ./submissions_api_tests.json \ + --global-var test_user=\$GWELLS_API_TEST_USER \ + --global-var test_password=\$GWELLS_API_TEST_PASSWORD \ + --global-var base_url=${BASEURL} \ + --global-var auth_server=\$GWELLS_API_TEST_AUTH_SERVER \ + --global-var client_id=\$GWELLS_API_TEST_CLIENT_ID \ + --global-var client_secret=\$GWELLS_API_TEST_CLIENT_SECRET \ + -r cli,junit,html + newman run ./aquifers_api_tests.json \ + --global-var test_user=\$GWELLS_API_TEST_USER \ + --global-var test_password=\$GWELLS_API_TEST_PASSWORD \ + --global-var base_url=${BASEURL} \ + --global-var auth_server=\$GWELLS_API_TEST_AUTH_SERVER \ + --global-var client_id=\$GWELLS_API_TEST_CLIENT_ID \ + --global-var client_secret=\$GWELLS_API_TEST_CLIENT_SECRET \ + -r cli,junit,html + """ + } finally { + junit 'newman/*.xml' + publishHTML ( + target: [ + allowMissing: false, + alwaysLinkToLastBuild: false, + keepAll: true, + reportDir: 'newman', + reportFiles: 'newman*.html', + reportName: "API Test Report" + ] + ) + stash includes: 'newman/*.xml', name: 'api-tests' + } + } // end dir + } //end node + } //end podTemplate + } //end stage + }, //end branch + "Code Quality": { + /* Code quality stage - pipeline step/closure + - unstash unit test results (previous stage) + - use SonarQube to consume results (*.xml) + */ + _stage('Code Quality', context) { + podTemplate( + name: "sonar-runner${context.uuid}", + label: "sonar-runner${context.uuid}", + serviceAccount: 'jenkins', + cloud: 'openshift', + containers:[ + containerTemplate( + name: 'jnlp', + resourceRequestMemory: '4Gi', + resourceLimitMemory: '4Gi', + resourceRequestCpu: '4000m', + resourceLimitCpu: '4000m', + image: 'registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7:v3.7', + workingDir: '/tmp', + args: '${computer.jnlpmac} ${computer.name}', + envVars: [ + envVar(key:'GRADLE_USER_HOME', value: '/var/cache/artifacts/gradle') + ] + ) + ], + volumes: [ + persistentVolumeClaim( + mountPath: '/var/cache/artifacts', + claimName: 'cache', + readOnly: false + ) + ] + ){ + node("sonar-runner${context.uuid}") { + //the checkout is mandatory, otherwise code quality check would fail + echo "checking out source" + echo "Build: ${BUILD_ID}" + checkout scm + + String SONARQUBE_URL = 'https://sonarqube-moe-gwells-tools.pathfinder.gov.bc.ca' + echo "SONARQUBE_URL: ${SONARQUBE_URL}" + waitUntil { + sleep 5 + return isUnitTested + } + dir('app') { + unstash 'nodejunit' + unstash 'nodecoverage' + } + dir('sonar-runner') { + unstash 'coverage' + sh script: + """ + ./gradlew -q dependencies + ./gradlew sonarqube -Dsonar.host.url=${SONARQUBE_URL} -Dsonar.verbose=true \ + --stacktrace --info -Dsonar.sources=.. + """, + returnStdout: true + } + } //end node + } //end podTemplate + } //end stage + } //end branch +) //end parallel + + +/* Continuous Deployment (CD) + For PRs to the master branch, reserved for release branches and hotfixes + Iterates through DEV (skipped), TEST and PROD environments + - [prompt/stop] + || deployment to persistent TEST environment (sets isDeployed=true) + || smoke tests (executes on isDeployed) + - [prompt/stop] + - deployment to persistent PROD environment + - GitHub tasks (merge, close PR, deleteproduction branch) +*/ +for(String envKeyName: context.env.keySet() as String[]){ + String stageDeployName=envKeyName.toUpperCase() + + if (!"DEV".equalsIgnoreCase(stageDeployName) && "master".equalsIgnoreCase(env.CHANGE_TARGET)) { + _stage("Approve - ${stageDeployName}", context) { + node('master') { + new OpenShiftHelper().waitUntilEnvironmentIsReady(this, context, envKeyName) + } + def inputResponse = null; + try{ + inputResponse = input( + id: "deploy_${stageDeployName.toLowerCase()}", + message: "Deploy to ${stageDeployName}?", + ok: 'Approve', + submitterParameter: 'approved_by' + ) + }catch(ex){ + error "Pipeline has been aborted. - ${ex}" + } + GitHubHelper.getPullRequest(this).comment( + "User '${inputResponse}' has approved deployment to '${stageDeployName}'" + ) + } + + isDeployed = false + parallel ( + "Deploy - ${stageDeployName}": { + _stage("Deploy - ${stageDeployName}", context) { + node('master') { + new OpenShiftHelper().deploy(this, context, envKeyName) + isDeployed = true + } + } + }, + "Smoke Test - ${stageDeployName}": { + waitUntil { + sleep 5 + return isDeployed + } + _stage("Smoke Test - ${stageDeployName}", context){ + String baseURL = context.deployments[envKeyName].environmentUrl.substring( + 0, + context.deployments[envKeyName].environmentUrl.indexOf('/', 8) + 1 + ) + podTemplate( + label: "bddstack-${context.uuid}", + name: "bddstack-${context.uuid}", + serviceAccount: 'jenkins', + cloud: 'openshift', + containers: [ + containerTemplate( + name: 'jnlp', + image: 'docker-registry.default.svc:5000/openshift/jenkins-slave-bddstack', + resourceRequestCpu: '800m', + resourceLimitCpu: '800m', + resourceRequestMemory: '3Gi', + resourceLimitMemory: '3Gi', + workingDir: '/home/jenkins', + command: '', + args: '${computer.jnlpmac} ${computer.name}', + envVars: [ + envVar(key:'BASEURL', value: baseURL), + envVar(key:'GRADLE_USER_HOME', value: '/var/cache/artifacts/gradle') + ] + ) + ], + volumes: [ + persistentVolumeClaim( + mountPath: '/var/cache/artifacts', + claimName: 'cache', + readOnly: false + ) + ] + ){ + node("bddstack-${context.uuid}") { + echo "Build: ${BUILD_ID}" + echo "baseURL: ${baseURL}" + checkout scm + dir('functional-tests') { + try { + sh './gradlew -DchromeHeadlessTest.single=WellDetails chromeHeadlessTest' + } finally { + archiveArtifacts allowEmptyArchive: true, artifacts: 'build/reports/geb/**/*' + junit testResults:'build/test-results/**/*.xml', allowEmptyResults:true + publishHTML ( + target: [ + allowMissing: true, + alwaysLinkToLastBuild: false, + keepAll: true, + reportDir: 'build/reports/spock', + reportFiles: 'index.html', + reportName: "Test: BDD Spock Report" + ] + ) + publishHTML ( + target: [ + allowMissing: true, + alwaysLinkToLastBuild: false, + keepAll: true, + reportDir: 'build/reports/tests/chromeHeadlessTest', + reportFiles: 'index.html', + reportName: "Test: Full Test Report" + ] + ) + } + } //end dir + } //end node + } //end podTemplate + } //end stage + } + ) + } //end if +} // end for + + +/* Cleanup stage - pipeline step/closure + - Prompt user to continue + - Remove temporary OpenShift resources (moe-gwells-dev) + - Merge and delete branches +*/ +stage('Cleanup') { + + def inputResponse = null + String mergeMethod='merge' + + waitUntil { + boolean isDone=false + try{ + inputResponse=input( + id: 'close_pr', + message: "Ready to Accept/Merge (using '${mergeMethod}' method), and Close pull-request #${env.CHANGE_ID}?", + ok: 'Yes', + submitter: 'authenticated', + submitterParameter: 'approver' + ) + echo "inputResponse:${inputResponse}" + + echo "Merging and Closing PR" + GitHubHelper.mergeAndClosePullRequest(this, mergeMethod) + + echo "Clearing OpenShift resources" + new OpenShiftHelper().cleanup(this, context) + + isDone=true + }catch (ex){ + echo "${stackTraceAsString(ex)}" + def inputAction = input( + message: "This 'Cleanup' stage has failed. See error above.", + ok: 'Confirm', + submitter: 'authenticated', + parameters: [ + choice( + name: 'action', + choices: 'Re-run\nIgnore', + description: 'What would you like to do?' + ) + ] + ) + if ('Ignore'.equalsIgnoreCase(inputAction)){ + isDone=true + } + } + return isDone + } //end waitUntil +} diff --git a/Makefile b/Makefile index 780b16dc5b..268132d12a 100644 --- a/Makefile +++ b/Makefile @@ -6,13 +6,13 @@ default: vue vue: prep - NPM_CMD=dev docker-compose up; docker-compose down + NPM_CMD=dev docker-compose up -d api: prep - NPM_CMD=build docker-compose up; docker-compose down + NPM_CMD=build docker-compose up -d django: prep - NPM_CMD=watch docker-compose up; docker-compose down + NPM_CMD=watch docker-compose up -d ################### @@ -23,6 +23,21 @@ prep: @ docker-compose pull @ docker-compose build +fixtures: + @ docker exec -ti gwells_api_1 bash -c " \ + cd /app/backend; \ + python manage.py migrate; \ + python manage.py loaddata gwells-codetables.json; \ + python manage.py loaddata wellsearch-codetables.json registries-codetables.json; \ + python manage.py loaddata wellsearch.json.gz registries.json; \ + python manage.py loaddata aquifers.json; \ + python manage.py createinitialrevisions \ + " || \ + echo "Failed. Please make sure the API container has had time to start." + +down: + @ docker-compose down + db-clean: @ docker-compose down || true @ rm -rf ./.tmp/psql-dev diff --git a/api-tests/.envrc b/api-tests/.envrc new file mode 100644 index 0000000000..5c931dcb22 --- /dev/null +++ b/api-tests/.envrc @@ -0,0 +1,10 @@ + +# Set variables required for newman api tests +#export GWELLS_API_BASE_URL=https://gwells-dev-pr-862-moe-gwells-dev.pathfinder.gov.bc.ca/gwells +#export GWELLS_API_BASE_URL=https://gwells-dev-pr-860-moe-gwells-dev.pathfinder.gov.bc.ca/gwells +export GWELLS_API_BASE_URL=http://127.0.0.1:8000/gwells +export GWELLS_API_TEST_USER=testuser +export GWELLS_API_TEST_AUTH_SERVER=https://sso-test.pathfinder.gov.bc.ca/auth/realms/gwells/protocol/openid-connect/token +export GWELLS_API_TEST_CLIENT_ID=gwells-api-test + +dotenv .secret_env \ No newline at end of file diff --git a/api-tests/aquifers_api_tests.json b/api-tests/aquifers_api_tests.json index 7246833962..f3b73244f7 100644 --- a/api-tests/aquifers_api_tests.json +++ b/api-tests/aquifers_api_tests.json @@ -43,6 +43,272 @@ }, "response": [] }, + { + "name": "Aquifer Codes / Materials List", + "event": [ + { + "listen": "test", + "script": { + "id": "85664ee3-92d9-4510-b7d8-6bd6c44ec870", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json()", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{base_url}}/api/v1/aquifer-codes/materials", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "aquifer-codes", + "materials" + ] + } + }, + "response": [] + }, + { + "name": "Aquifer Codes / Quality Concerns List", + "event": [ + { + "listen": "test", + "script": { + "id": "85664ee3-92d9-4510-b7d8-6bd6c44ec870", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json()", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{base_url}}/api/v1/aquifer-codes/quality-concerns", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "aquifer-codes", + "quality-concerns" + ] + } + }, + "response": [] + }, + { + "name": "Aquifer Codes / Vulnerability List", + "event": [ + { + "listen": "test", + "script": { + "id": "85664ee3-92d9-4510-b7d8-6bd6c44ec870", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json()", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{base_url}}/api/v1/aquifer-codes/vulnerability", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "aquifer-codes", + "vulnerability" + ] + } + }, + "response": [] + }, + { + "name": "Aquifer Codes / Subtypes List", + "event": [ + { + "listen": "test", + "script": { + "id": "85664ee3-92d9-4510-b7d8-6bd6c44ec870", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json()", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{base_url}}/api/v1/aquifer-codes/subtypes", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "aquifer-codes", + "subtypes" + ] + } + }, + "response": [] + }, + { + "name": "Aquifer Codes / Productivity List", + "event": [ + { + "listen": "test", + "script": { + "id": "85664ee3-92d9-4510-b7d8-6bd6c44ec870", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json()", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{base_url}}/api/v1/aquifer-codes/productivity", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "aquifer-codes", + "productivity" + ] + } + }, + "response": [] + }, + { + "name": "Aquifer Codes / Demand List", + "event": [ + { + "listen": "test", + "script": { + "id": "85664ee3-92d9-4510-b7d8-6bd6c44ec870", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json()", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{base_url}}/api/v1/aquifer-codes/demand", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "aquifer-codes", + "demand" + ] + } + }, + "response": [] + }, + { + "name": "Aquifer Codes / Water Use List", + "event": [ + { + "listen": "test", + "script": { + "id": "85664ee3-92d9-4510-b7d8-6bd6c44ec870", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json()", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{base_url}}/api/v1/aquifer-codes/water-use", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "aquifer-codes", + "water-use" + ] + } + }, + "response": [] + }, { "name": "Aquifers Search", "event": [ @@ -130,6 +396,44 @@ } }, "response": [] + }, + { + "name": "Aquifer Retrieve", + "event": [ + { + "listen": "test", + "script": { + "id": "768133b0-7dd6-4197-97ca-ddca5cb1743f", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json()", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{base_url}}/api/v1/aquifers/1", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "aquifers", + "1" + ] + } + }, + "response": [] } ] } \ No newline at end of file diff --git a/api-tests/local_aquifers.sh b/api-tests/local_aquifers.sh new file mode 100755 index 0000000000..b3c2060b28 --- /dev/null +++ b/api-tests/local_aquifers.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# +# Usage: +# - Set env variables (choose any username and password): +# GWELLS_API_TEST_USER: test account username +# GWELLS_API_TEST_PASSWORD: test account password +# GWELLS_API_BASE_URL: URL for testing (API tests will make requests here) +# GWELLS_API_TEST_AUTH_SERVER: SSO auth server (GWELLS realm) +# GWELLS_API_TEST_CLIENT_ID: SSO test client ID (the client set up on the GWELLS realm for testing) +# GWELLS_API_TEST_CLIENT_SECRET: SSO test client secret +# - Install newman: +# npm install -g newman +# - Run script: +# ./local_newman.sh + + +ENV_VARS=( + "GWELLS_API_TEST_USER" + "GWELLS_API_TEST_PASSWORD" + "GWELLS_API_BASE_URL" + "GWELLS_API_TEST_AUTH_SERVER" + "GWELLS_API_TEST_CLIENT_ID" + "GWELLS_API_TEST_CLIENT_SECRET" +) + +for env_var in ${ENV_VARS[@]} +do + if [ -z ${!env_var+x} ]; then + echo "$env_var is unset" + exit + fi +done + +echo "Remember to install newman (npm install -g newman) and set GWELLS_API_TEST_USER," +echo "GWELLS_API_TEST_PASSWORD, GWELLS_API_BASE_URL and Keycloak credentials" +newman run ./aquifers_api_tests.json --global-var test_user=$GWELLS_API_TEST_USER --global-var test_password=$GWELLS_API_TEST_PASSWORD --global-var base_url=$GWELLS_API_BASE_URL --global-var auth_server=$GWELLS_API_TEST_AUTH_SERVER --global-var client_id=$GWELLS_API_TEST_CLIENT_ID --global-var client_secret=$GWELLS_API_TEST_CLIENT_SECRET diff --git a/api-tests/submissions_api_tests.json b/api-tests/submissions_api_tests.json index a337e36057..0c7b0abd61 100644 --- a/api-tests/submissions_api_tests.json +++ b/api-tests/submissions_api_tests.json @@ -95,859 +95,1718 @@ "name": "Activity Submissions", "item": [ { - "name": "Submission form Options", - "event": [ - { - "listen": "test", - "script": { - "id": "20837dbe-0390-4709-b8dc-4ac3b22153f4", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json()", - "", - "pm.test(\"Status code is 200\", function () {", - " pm.expect(pm.response.code).to.equal(200);", - "})", - "" - ] - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{base_url}}/api/v1/submissions/options/?format=json", - "host": [ - "{{base_url}}" - ], - "path": [ - "api", - "v1", - "submissions", - "options", - "" - ], - "query": [ - { - "key": "format", - "value": "json" - } - ] - }, - "description": "Get form submission options" - }, - "response": [] - }, - { - "name": "Submission list", - "event": [ - { - "listen": "test", - "script": { - "id": "20837dbe-0390-4709-b8dc-4ac3b22153f4", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json()", - "", - "pm.test(\"Status code is 200\", function () {", - " pm.expect(pm.response.code).to.equal(200);", - "})", - "" - ] - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", - "host": [ - "{{base_url}}" - ], - "path": [ - "api", - "v1", - "submissions", - "" - ], - "query": [ - { - "key": "format", - "value": "json" - } - ] - }, - "description": "Request a list of wells" - }, - "response": [] - }, - { - "name": "Create new driller for submission", - "event": [ - { - "listen": "test", - "script": { - "id": "05a0005d-cae7-447a-b666-63de221afd22", - "type": "text/javascript", - "exec": [ - "( function() {", - " pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([201,202]);", - " });", - " ", - " var jsonData = pm.response.json()", - " pm.environment.set(\"created_driller_guid\", jsonData.person_guid);", - " pm.environment.set(\"driller_audit_name\", jsonData.create_user);", - " ", - " pm.test(\"The record's identifier is in UUID format\", function () {", - " var uuid_re = /\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}/;", - " var match = uuid_re.exec(jsonData.person_guid);", - " pm.expect(match).to.be.ok;", - " pm.expect(match.length).to.be.ok;", - " pm.expect(match[0]).to.eql(jsonData.person_guid);", - " });", - " ", - " pm.test(\"Person first name equals name supplied in request\", function () {", - " pm.expect(jsonData.first_name).to.eql(\"Bobby\");", - " });", - " ", - " pm.test(\"Last name equals value supplied in request\", function () {", - " pm.expect(jsonData.surname).to.eql(\"Driller\");", - " });", - " ", - " pm.test(\"Audit fields (create_user, create_date) were filled in by backend\", function () {", - " pm.expect(jsonData.create_user.length).to.be.ok;", - " pm.expect(jsonData.create_date.length).to.be.ok;", - " });", - "})();", - "" - ] - } - } - ], - "request": { - "auth": { - "type": "noauth" - }, - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" - } - ], - "body": { - "mode": "formdata", - "formdata": [ - { - "key": "first_name", - "value": "Bobby", - "type": "text" - }, - { - "key": "surname", - "value": "Driller", - "type": "text" - } - ] - }, - "url": { - "raw": "{{base_url}}/api/v1/drillers/?format=json", - "host": [ - "{{base_url}}" - ], - "path": [ - "api", - "v1", - "drillers", - "" - ], - "query": [ - { - "key": "format", - "value": "json" - } - ] - }, - "description": "Create a new driller to be registered" - }, - "response": [] - }, - { - "name": "Submission", - "event": [ - { - "listen": "test", - "script": { - "id": "465f6140-5cef-4ee6-b5ff-8135e63f1903", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "", - "pm.environment.set(\"well_tag_number\", jsonData.well);", - "", - "pm.test(\"Status code is 201\", function () {", - " pm.expect(pm.response.code).to.equal(201);", - "})", - "", - "pm.test(\"Expect well record to be created\", function() {", - " pm.expect(jsonData.well).to.be.ok;", - "})", - "" - ] - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" - }, - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ], - "body": { - "mode": "urlencoded", - "urlencoded": [ - { - "key": "well_activity_type", - "value": "CON", - "type": "text" - }, - { - "key": "driller_responsible", - "value": "{{created_driller_guid}}", - "type": "text" - }, - { - "key": "work_end_date", - "value": "2018-07-02", - "type": "text" - }, - { - "key": "owner_full_name", - "value": "Holly Homeowner", - "type": "text" - }, - { - "key": "work_start_date", - "value": "2018-07-01", - "type": "text" - }, - { - "key": "owner_province_state", - "value": "BC", - "type": "text" - }, - { - "key": "owner_mailing_address", - "value": "321 Main Street", - "type": "text" - }, - { - "key": "owner_city", - "value": "Anytown", - "type": "text" - } - ] - }, - "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", - "host": [ - "{{base_url}}" - ], - "path": [ - "api", - "v1", - "submissions", - "" - ], - "query": [ - { - "key": "format", - "value": "json" - } - ] - }, - "description": "Submit a submission" - }, - "response": [] - }, - { - "name": "Check submission - created well", - "event": [ - { - "listen": "test", - "script": { - "id": "8cda82f1-4f66-419a-9d79-636186a44b6c", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "", - "pm.test(\"Status code is 200\", function () {", - " pm.expect(pm.response.code).to.equal(200);", - "})", - "", - "pm.test(\"Expect casing list to be empty\", function() {", - " pm.expect(jsonData.casing_set.length).to.equal(0);", - "})", - "", - "pm.test(\"Expect screen list empty\", function() {", - " pm.expect(jsonData.screen_set.length).to.equal(0);", - "})", - "", - "pm.test(\"Expect linerperforation to be empty\", function() {", - " pm.expect(jsonData.linerperforation_set.length).to.equal(0);", - "})" - ] - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" - }, - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ], - "body": { - "mode": "urlencoded", - "urlencoded": [ - { - "key": "well_activity_type", - "value": "CON", - "type": "text" - }, - { - "key": "driller_responsible", - "value": "{{created_driller_guid}}", - "type": "text" - }, - { - "key": "work_end_date", - "value": "2018-07-02", - "type": "text" - }, - { - "key": "owner_full_name", - "value": "Holly Homeowner", - "type": "text" - }, - { - "key": "work_start_date", - "value": "2018-07-01", - "type": "text" - }, - { - "key": "owner_province_state", - "value": "BC", - "type": "text" - }, - { - "key": "owner_mailing_address", - "value": "321 Main Street", - "type": "text" - }, - { - "key": "owner_city", - "value": "Anytown", - "type": "text" + "name": "General", + "item": [ + { + "name": "Submission form Options", + "event": [ + { + "listen": "test", + "script": { + "id": "20837dbe-0390-4709-b8dc-4ac3b22153f4", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json()", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "" + ] + } } - ] - }, - "url": { - "raw": "{{base_url}}/api/v1/wells/{{well_tag_number}}", - "host": [ - "{{base_url}}" ], - "path": [ - "api", - "v1", - "wells", - "{{well_tag_number}}" - ] - }, - "description": "Check if the created well matched submission" - }, - "response": [] - }, - { - "name": "Submission - casings", - "event": [ - { - "listen": "test", - "script": { - "id": "c6ccd3af-884d-45fb-93f4-21294aa25b3a", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "", - "pm.environment.set(\"well_tag_number\", jsonData.well);", - "", - "pm.test(\"Status code is 201\", function () {", - " pm.expect(pm.response.code).to.equal(201);", - "})", - "", - "pm.test(\"Expect casings records to be created\", function() {", - " pm.expect(jsonData.casing_set.length).to.equal(2);", - "})", - "", - "" - ] - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{base_url}}/api/v1/submissions/options/?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "options", + "" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] + }, + "description": "Get form submission options" }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"casing_set\": [\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"1\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"1\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t},\n\t\t{\n\t\t\t\"casing_code\":\"SURFACE\",\n\t\t\t\"start\":\"2\",\n\t\t\t\"casing_material\":\"STEEL\",\n\t\t\t\"end\":\"2\",\n\t\t\t\"diameter\":\"2\",\n\t\t\t\"drive_shoe\":\"True\",\n\t\t\t\"id\":1,\n\t\t\t\"wall_thickness\":\"7\"\n\t\t}\n\t]\n}" + "response": [] }, - "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", - "host": [ - "{{base_url}}" - ], - "path": [ - "api", - "v1", - "submissions", - "" - ], - "query": [ - { - "key": "format", - "value": "json" + { + "name": "Submission list", + "event": [ + { + "listen": "test", + "script": { + "id": "20837dbe-0390-4709-b8dc-4ac3b22153f4", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json()", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "" + ] + } } - ] + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{base_url}}/api/v1/submissions/?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] + }, + "description": "Request a list of wells" + }, + "response": [] }, - "description": "Request a list of wells" - }, - "response": [] - }, - { - "name": "Submission - casings - empty", - "event": [ { - "listen": "test", - "script": { - "id": "bf57abbb-38dd-41f5-a5bc-b8b85d729ade", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "", - "pm.test(\"Status code is 400\", function () {", - " pm.expect(pm.response.code).to.equal(400);", - "})", - "" - ] - } + "name": "Create new driller for submission", + "event": [ + { + "listen": "test", + "script": { + "id": "05a0005d-cae7-447a-b666-63de221afd22", + "type": "text/javascript", + "exec": [ + "( function() {", + " pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([201,202]);", + " });", + " ", + " var jsonData = pm.response.json()", + " pm.environment.set(\"created_driller_guid\", jsonData.person_guid);", + " pm.environment.set(\"driller_audit_name\", jsonData.create_user);", + " ", + " pm.test(\"The record's identifier is in UUID format\", function () {", + " var uuid_re = /\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}/;", + " var match = uuid_re.exec(jsonData.person_guid);", + " pm.expect(match).to.be.ok;", + " pm.expect(match.length).to.be.ok;", + " pm.expect(match[0]).to.eql(jsonData.person_guid);", + " });", + " ", + " pm.test(\"Person first name equals name supplied in request\", function () {", + " pm.expect(jsonData.first_name).to.eql(\"Bobby\");", + " });", + " ", + " pm.test(\"Last name equals value supplied in request\", function () {", + " pm.expect(jsonData.surname).to.eql(\"Driller\");", + " });", + " ", + " pm.test(\"Audit fields (create_user, create_date) were filled in by backend\", function () {", + " pm.expect(jsonData.create_user.length).to.be.ok;", + " pm.expect(jsonData.create_date.length).to.be.ok;", + " });", + "})();", + "" + ] + } + } + ], + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "first_name", + "value": "Bobby", + "type": "text" + }, + { + "key": "surname", + "value": "Driller", + "type": "text" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/v1/drillers/?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "drillers", + "" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] + }, + "description": "Create a new driller to be registered" + }, + "response": [] } ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"casing_set\": [\n\t\t{\n\t\t},\n\t\t{\n\t\t}\n\t]\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", - "host": [ - "{{base_url}}" - ], - "path": [ - "api", - "v1", - "submissions", - "" - ], - "query": [ - { - "key": "format", - "value": "json" - } - ] - }, - "description": "Request a list of wells" - }, - "response": [] + "_postman_isSubFolder": true }, { - "name": "Check submission - created well casings", - "event": [ + "name": "Construction", + "item": [ { - "listen": "test", - "script": { - "id": "31a610c9-915e-4873-931a-613c8b1cfde4", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "", - "pm.test(\"Status code is 200\", function () {", - " pm.expect(pm.response.code).to.equal(200);", - "})", - "", - "pm.test(\"Expect casing list to contain two entries\", function() {", - " pm.expect(jsonData.casing_set.length).to.equal(2);", - "})", - "", - "pm.test(\"Expect screen list empty\", function() {", - " pm.expect(jsonData.screen_set.length).to.equal(0);", - "})", - "", - "pm.test(\"Expect linerperforation to be empty\", function() {", - " pm.expect(jsonData.linerperforation_set.length).to.equal(0);", - "})" - ] - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" - }, - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ], - "body": { - "mode": "urlencoded", - "urlencoded": [ - { - "key": "well_activity_type", - "value": "CON", - "type": "text" - }, - { - "key": "driller_responsible", - "value": "{{created_driller_guid}}", - "type": "text" - }, - { - "key": "work_end_date", - "value": "2018-07-02", - "type": "text" - }, - { - "key": "owner_full_name", - "value": "Holly Homeowner", - "type": "text" + "name": "Construction Submission", + "event": [ + { + "listen": "test", + "script": { + "id": "74cf621d-6689-43e8-be83-f6b1cb4d5d0a", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.environment.set(\"well_tag_number\", jsonData.well);", + "pm.environment.set('filing_number', jsonData.filing_number)", + "", + "pm.test(\"Status code is 201\", function () {", + " pm.expect(pm.response.code).to.equal(201);", + "})", + "", + "pm.test(\"Expect well record to be created\", function() {", + " pm.expect(jsonData.well).to.be.ok;", + "})", + "" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "driller_responsible", + "value": "{{created_driller_guid}}", + "type": "text" + }, + { + "key": "work_end_date", + "value": "2018-07-02", + "type": "text" + }, + { + "key": "owner_full_name", + "value": "Holly Homeowner", + "type": "text" + }, + { + "key": "work_start_date", + "value": "2018-07-01", + "type": "text" + }, + { + "key": "owner_province_state", + "value": "BC", + "type": "text" + }, + { + "key": "owner_mailing_address", + "value": "321 Main Street", + "type": "text" + }, + { + "key": "owner_city", + "value": "Anytown", + "type": "text" + } + ] }, - { - "key": "work_start_date", - "value": "2018-07-01", - "type": "text" + "url": { + "raw": "{{base_url}}/api/v1/submissions/construction?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "construction" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] }, - { - "key": "owner_province_state", - "value": "BC", - "type": "text" + "description": "Submit a submission" + }, + "response": [] + }, + { + "name": "Check submission - created well", + "event": [ + { + "listen": "test", + "script": { + "id": "8cda82f1-4f66-419a-9d79-636186a44b6c", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "", + "pm.test(\"Expect casing list to be empty\", function() {", + " pm.expect(jsonData.casing_set.length).to.equal(0);", + "})", + "", + "pm.test(\"Expect screen list empty\", function() {", + " pm.expect(jsonData.screen_set.length).to.equal(0);", + "})", + "", + "pm.test(\"Expect linerperforation to be empty\", function() {", + " pm.expect(jsonData.linerperforation_set.length).to.equal(0);", + "})" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "well_activity_type", + "value": "CON", + "type": "text" + }, + { + "key": "driller_responsible", + "value": "{{created_driller_guid}}", + "type": "text" + }, + { + "key": "work_end_date", + "value": "2018-07-02", + "type": "text" + }, + { + "key": "owner_full_name", + "value": "Holly Homeowner", + "type": "text" + }, + { + "key": "work_start_date", + "value": "2018-07-01", + "type": "text" + }, + { + "key": "owner_province_state", + "value": "BC", + "type": "text" + }, + { + "key": "owner_mailing_address", + "value": "321 Main Street", + "type": "text" + }, + { + "key": "owner_city", + "value": "Anytown", + "type": "text" + } + ] }, - { - "key": "owner_mailing_address", - "value": "321 Main Street", - "type": "text" + "url": { + "raw": "{{base_url}}/api/v1/wells/{{well_tag_number}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "wells", + "{{well_tag_number}}" + ] }, - { - "key": "owner_city", - "value": "Anytown", - "type": "text" - } - ] + "description": "Check if the created well matched submission" + }, + "response": [] }, - "url": { - "raw": "{{base_url}}/api/v1/wells/{{well_tag_number}}", - "host": [ - "{{base_url}}" + { + "name": "Check submission - created construction submission", + "event": [ + { + "listen": "test", + "script": { + "id": "8cda82f1-4f66-419a-9d79-636186a44b6c", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "", + "pm.test(\"Expect casing list to be empty\", function() {", + " pm.expect(jsonData.casing_set.length).to.equal(0);", + "})", + "", + "pm.test(\"Expect screen list empty\", function() {", + " pm.expect(jsonData.screen_set.length).to.equal(0);", + "})", + "", + "pm.test(\"Expect linerperforation to be empty\", function() {", + " pm.expect(jsonData.linerperforation_set.length).to.equal(0);", + "})" + ] + } + } ], - "path": [ - "api", - "v1", - "wells", - "{{well_tag_number}}" - ] + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "well_activity_type", + "value": "CON", + "type": "text" + }, + { + "key": "driller_responsible", + "value": "{{created_driller_guid}}", + "type": "text" + }, + { + "key": "work_end_date", + "value": "2018-07-02", + "type": "text" + }, + { + "key": "owner_full_name", + "value": "Holly Homeowner", + "type": "text" + }, + { + "key": "work_start_date", + "value": "2018-07-01", + "type": "text" + }, + { + "key": "owner_province_state", + "value": "BC", + "type": "text" + }, + { + "key": "owner_mailing_address", + "value": "321 Main Street", + "type": "text" + }, + { + "key": "owner_city", + "value": "Anytown", + "type": "text" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/v1/submissions/{{filing_number}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "{{filing_number}}" + ] + }, + "description": "Check if the submission was created correctly." + }, + "response": [] }, - "description": "Check if the created well matched submission" - }, - "response": [] - }, - { - "name": "Submission - screen", - "event": [ { - "listen": "test", - "script": { - "id": "eaf2d1fa-c857-4c87-b7a5-bab319649826", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "", - "pm.environment.set(\"well_tag_number\", jsonData.well);", - "", - "pm.test(\"Status code is 201\", function() {", - " pm.expect(pm.response.code).to.equal(201);", - "})" - ] - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" + "name": "Construction Submission - casings", + "event": [ + { + "listen": "test", + "script": { + "id": "c6ccd3af-884d-45fb-93f4-21294aa25b3a", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.environment.set(\"well_tag_number\", jsonData.well);", + "", + "pm.test(\"Status code is 201\", function () {", + " pm.expect(pm.response.code).to.equal(201);", + "})", + "", + "pm.test(\"Expect casings records to be created\", function() {", + " pm.expect(jsonData.casing_set.length).to.equal(2);", + "})", + "", + "" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"casing_set\": [\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"1\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"1\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t},\n\t\t{\n\t\t\t\"casing_code\":\"SURFACE\",\n\t\t\t\"start\":\"2\",\n\t\t\t\"casing_material\":\"STEEL\",\n\t\t\t\"end\":\"2\",\n\t\t\t\"diameter\":\"2\",\n\t\t\t\"drive_shoe\":\"True\",\n\t\t\t\"id\":1,\n\t\t\t\"wall_thickness\":\"7\"\n\t\t}\n\t]\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/submissions/construction?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "construction" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] + }, + "description": "Request a list of wells" }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"liner_diameter\": 1,\n\t\"liner_from\": 0,\n\t\"liner_material\": \"PVC\",\n\t\"liner_thickness\": 2,\n\t\"liner_to\": 3,\n\t\"screen_set\": [\n\t\t{\n\t\t\t\"start\": 10,\n\t\t\t\"end\": 20,\n\t\t\t\"assembly_type\": \"SCREEN\"\n\t\t}\n\t]\n}" + "response": [] }, - "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", - "host": [ - "{{base_url}}" + { + "name": "Construction Submission - casings - empty", + "event": [ + { + "listen": "test", + "script": { + "id": "bf57abbb-38dd-41f5-a5bc-b8b85d729ade", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 400\", function () {", + " pm.expect(pm.response.code).to.equal(400);", + "})", + "" + ] + } + } ], - "path": [ - "api", - "v1", - "submissions", - "" + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"casing_set\": [\n\t\t{\n\t\t},\n\t\t{\n\t\t}\n\t]\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/submissions/construction?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "construction" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] + }, + "description": "Request a list of wells" + }, + "response": [] + }, + { + "name": "Check submission - created well casings", + "event": [ + { + "listen": "test", + "script": { + "id": "31a610c9-915e-4873-931a-613c8b1cfde4", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "", + "pm.test(\"Expect casing list to contain two entries\", function() {", + " pm.expect(jsonData.casing_set.length).to.equal(2);", + "})", + "", + "pm.test(\"Expect screen list empty\", function() {", + " pm.expect(jsonData.screen_set.length).to.equal(0);", + "})", + "", + "pm.test(\"Expect linerperforation to be empty\", function() {", + " pm.expect(jsonData.linerperforation_set.length).to.equal(0);", + "})" + ] + } + } ], - "query": [ - { - "key": "format", - "value": "json" + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "well_activity_type", + "value": "CON", + "type": "text" + }, + { + "key": "driller_responsible", + "value": "{{created_driller_guid}}", + "type": "text" + }, + { + "key": "work_end_date", + "value": "2018-07-02", + "type": "text" + }, + { + "key": "owner_full_name", + "value": "Holly Homeowner", + "type": "text" + }, + { + "key": "work_start_date", + "value": "2018-07-01", + "type": "text" + }, + { + "key": "owner_province_state", + "value": "BC", + "type": "text" + }, + { + "key": "owner_mailing_address", + "value": "321 Main Street", + "type": "text" + }, + { + "key": "owner_city", + "value": "Anytown", + "type": "text" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/v1/wells/{{well_tag_number}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "wells", + "{{well_tag_number}}" + ] + }, + "description": "Check if the created well matched submission" + }, + "response": [] + }, + { + "name": "Construction Submission - screen", + "event": [ + { + "listen": "test", + "script": { + "id": "eaf2d1fa-c857-4c87-b7a5-bab319649826", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.environment.set(\"well_tag_number\", jsonData.well);", + "", + "pm.test(\"Status code is 201\", function() {", + " pm.expect(pm.response.code).to.equal(201);", + "})" + ] + } } - ] + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"liner_diameter\": 1,\n\t\"liner_from\": 0,\n\t\"liner_material\": \"PVC\",\n\t\"liner_thickness\": 2,\n\t\"liner_to\": 3,\n\t\"screen_set\": [\n\t\t{\n\t\t\t\"start\": 10,\n\t\t\t\"end\": 20,\n\t\t\t\"assembly_type\": \"SCREEN\"\n\t\t}\n\t]\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/submissions/construction?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "construction" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] + }, + "description": "Request a list of wells" + }, + "response": [] }, - "description": "Request a list of wells" - }, - "response": [] - }, - { - "name": "Submission - screen empty", - "event": [ { - "listen": "test", - "script": { - "id": "78582a37-9917-4d5c-a736-a0db198978ef", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "", - "pm.test(\"Status code is 400\", function() {", - " pm.expect(pm.response.code).to.equal(400);", - "})" - ] - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" + "name": "Construction Submission - screen empty", + "event": [ + { + "listen": "test", + "script": { + "id": "78582a37-9917-4d5c-a736-a0db198978ef", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 400\", function() {", + " pm.expect(pm.response.code).to.equal(400);", + "})" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"liner_diameter\": 1,\n\t\"liner_from\": 0,\n\t\"liner_material\": \"PVC\",\n\t\"liner_thickness\": 2,\n\t\"liner_to\": 3,\n\t\"screen_set\": [\n\t\t{\n\t\t}\n\t]\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/submissions/construction?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "construction" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] + }, + "description": "Request a list of wells" }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"liner_diameter\": 1,\n\t\"liner_from\": 0,\n\t\"liner_material\": \"PVC\",\n\t\"liner_thickness\": 2,\n\t\"liner_to\": 3,\n\t\"screen_set\": [\n\t\t{\n\t\t}\n\t]\n}" + "response": [] }, - "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", - "host": [ - "{{base_url}}" + { + "name": "Check submission - created well screens", + "event": [ + { + "listen": "test", + "script": { + "id": "8d6d5f99-b61a-48a5-b3e3-c793a81a5622", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "", + "pm.test(\"Expect casing list to contain two entries\", function() {", + " pm.expect(jsonData.casing_set.length).to.equal(0);", + "})", + "", + "pm.test(\"Expect screen list empty\", function() {", + " pm.expect(jsonData.screen_set.length).to.equal(1);", + "})", + "", + "pm.test(\"Expect linerperforation to be empty\", function() {", + " pm.expect(jsonData.linerperforation_set.length).to.equal(0);", + "})" + ] + } + } ], - "path": [ - "api", - "v1", - "submissions", - "" + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "well_activity_type", + "value": "CON", + "type": "text" + }, + { + "key": "driller_responsible", + "value": "{{created_driller_guid}}", + "type": "text" + }, + { + "key": "work_end_date", + "value": "2018-07-02", + "type": "text" + }, + { + "key": "owner_full_name", + "value": "Holly Homeowner", + "type": "text" + }, + { + "key": "work_start_date", + "value": "2018-07-01", + "type": "text" + }, + { + "key": "owner_province_state", + "value": "BC", + "type": "text" + }, + { + "key": "owner_mailing_address", + "value": "321 Main Street", + "type": "text" + }, + { + "key": "owner_city", + "value": "Anytown", + "type": "text" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/v1/wells/{{well_tag_number}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "wells", + "{{well_tag_number}}" + ] + }, + "description": "Check if the created well matched submission" + }, + "response": [] + }, + { + "name": "Construction Submission - liner", + "event": [ + { + "listen": "test", + "script": { + "id": "af2703e6-b88c-41e0-a74f-14fdd2b1f927", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.environment.set(\"well_tag_number\", jsonData.well);", + "", + "pm.test(\"Status code is 201\", function () {", + " pm.expect(pm.response.code).to.equal(201);", + "})", + "" + ] + } + } ], - "query": [ - { - "key": "format", - "value": "json" + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"liner_diameter\": 1,\n\t\"liner_from\": 0,\n\t\"liner_material\": \"PVC\",\n\t\"liner_thickness\": 2,\n\t\"liner_to\": 3,\n\t\"linerperforation_set\": [\n\t\t{\n\t\t\t\"start\": 0,\n\t\t\t\"end\": 10\n\t\t}\n\t]\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/submissions/construction?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "construction" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] + }, + "description": "Request a list of wells" + }, + "response": [] + }, + { + "name": "Construction Submission - liner empty", + "event": [ + { + "listen": "test", + "script": { + "id": "3737032f-d35f-4597-a306-133d74120ae4", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 400\", function () {", + " pm.expect(pm.response.code).to.equal(400);", + "})", + "" + ] + } } - ] + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"liner_diameter\": 1,\n\t\"liner_from\": 0,\n\t\"liner_material\": \"PVC\",\n\t\"liner_thickness\": 2,\n\t\"liner_to\": 3,\n\t\"linerperforation_set\": [\n\t\t{\n\t\t}\n\t]\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/submissions/construction?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "construction" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] + }, + "description": "Request a list of wells" + }, + "response": [] }, - "description": "Request a list of wells" - }, - "response": [] - }, - { - "name": "Check submission - created well screens", - "event": [ { - "listen": "test", - "script": { - "id": "8d6d5f99-b61a-48a5-b3e3-c793a81a5622", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "", - "pm.test(\"Status code is 200\", function () {", - " pm.expect(pm.response.code).to.equal(200);", - "})", - "", - "pm.test(\"Expect casing list to contain two entries\", function() {", - " pm.expect(jsonData.casing_set.length).to.equal(0);", - "})", - "", - "pm.test(\"Expect screen list empty\", function() {", - " pm.expect(jsonData.screen_set.length).to.equal(1);", - "})", - "", - "pm.test(\"Expect linerperforation to be empty\", function() {", - " pm.expect(jsonData.linerperforation_set.length).to.equal(0);", - "})" - ] - } + "name": "Check submission - created well liners", + "event": [ + { + "listen": "test", + "script": { + "id": "77ba3bf1-a4ad-476e-bd9a-c67bf0e7a34c", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "", + "pm.test(\"Expect casing list to contain two entries\", function() {", + " pm.expect(jsonData.casing_set.length).to.equal(0);", + "})", + "", + "pm.test(\"Expect screen list empty\", function() {", + " pm.expect(jsonData.screen_set.length).to.equal(0);", + "})", + "", + "pm.test(\"Expect linerperforation to be empty\", function() {", + " pm.expect(jsonData.linerperforation_set.length).to.equal(1);", + "})" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "well_activity_type", + "value": "CON", + "type": "text" + }, + { + "key": "driller_responsible", + "value": "{{created_driller_guid}}", + "type": "text" + }, + { + "key": "work_end_date", + "value": "2018-07-02", + "type": "text" + }, + { + "key": "owner_full_name", + "value": "Holly Homeowner", + "type": "text" + }, + { + "key": "work_start_date", + "value": "2018-07-01", + "type": "text" + }, + { + "key": "owner_province_state", + "value": "BC", + "type": "text" + }, + { + "key": "owner_mailing_address", + "value": "321 Main Street", + "type": "text" + }, + { + "key": "owner_city", + "value": "Anytown", + "type": "text" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/v1/wells/{{well_tag_number}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "wells", + "{{well_tag_number}}" + ] + }, + "description": "Check if the created well matched submission" + }, + "response": [] } ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" - }, - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ], - "body": { - "mode": "urlencoded", - "urlencoded": [ - { - "key": "well_activity_type", - "value": "CON", - "type": "text" - }, - { - "key": "driller_responsible", - "value": "{{created_driller_guid}}", - "type": "text" + "_postman_isSubFolder": true + }, + { + "name": "Alteration", + "item": [ + { + "name": "Alteration for unknown well", + "event": [ + { + "listen": "test", + "script": { + "id": "5cb93a2d-3c82-4504-9f6f-c68857e2f8d4", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.environment.set(\"well_tag_number\", jsonData.well);", + "pm.environment.set('filing_number', jsonData.filing_number)", + "", + "pm.test(\"Status code is 201\", function () {", + " pm.expect(pm.response.code).to.equal(201);", + "})", + "", + "" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"well_activity_type\": \"ALT\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"ALT_WELL_TEST_CITY\",\n\t\"owner_province_state\": \"BC\",\n\t\"casing_set\": [\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"10\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"20\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t}\n\t],\n \"linerperforation_set\": [\n\t\t{\n\t\t\t\"start\": 10,\n\t\t\t\"end\": 20\n\t\t}\n\t]\n}" }, - { - "key": "work_end_date", - "value": "2018-07-02", - "type": "text" + "url": { + "raw": "{{base_url}}/api/v1/submissions/alteration?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "alteration" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] }, - { - "key": "owner_full_name", - "value": "Holly Homeowner", - "type": "text" + "description": "Request a list of wells" + }, + "response": [] + }, + { + "name": "Check submission - well created - Alteration report on unknown well", + "event": [ + { + "listen": "test", + "script": { + "id": "758668aa-93ed-4341-9c01-1eb1c25985dd", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "", + "pm.test(\"Expected well owner city to match the submitted alteration report\", function() {", + " pm.expect(jsonData.owner_city).to.equal(\"ALT_WELL_TEST_CITY\");", + "})", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "well_activity_type", + "value": "CON", + "type": "text" + }, + { + "key": "driller_responsible", + "value": "{{created_driller_guid}}", + "type": "text" + }, + { + "key": "work_end_date", + "value": "2018-07-02", + "type": "text" + }, + { + "key": "owner_full_name", + "value": "Holly Homeowner", + "type": "text" + }, + { + "key": "work_start_date", + "value": "2018-07-01", + "type": "text" + }, + { + "key": "owner_province_state", + "value": "BC", + "type": "text" + }, + { + "key": "owner_mailing_address", + "value": "321 Main Street", + "type": "text" + }, + { + "key": "owner_city", + "value": "Anytown", + "type": "text" + } + ] }, - { - "key": "work_start_date", - "value": "2018-07-01", - "type": "text" + "url": { + "raw": "{{base_url}}/api/v1/wells/{{well_tag_number}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "wells", + "{{well_tag_number}}" + ] }, - { - "key": "owner_province_state", - "value": "BC", - "type": "text" + "description": "Check if the created well matched submission" + }, + "response": [] + }, + { + "name": "Check submission - created alteration submission", + "event": [ + { + "listen": "test", + "script": { + "id": "758668aa-93ed-4341-9c01-1eb1c25985dd", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "", + "pm.test(\"Expected well owner city to match the submitted alteration report\", function() {", + " pm.expect(jsonData.owner_city).to.equal(\"ALT_WELL_TEST_CITY\");", + "})", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "well_activity_type", + "value": "CON", + "type": "text" + }, + { + "key": "driller_responsible", + "value": "{{created_driller_guid}}", + "type": "text" + }, + { + "key": "work_end_date", + "value": "2018-07-02", + "type": "text" + }, + { + "key": "owner_full_name", + "value": "Holly Homeowner", + "type": "text" + }, + { + "key": "work_start_date", + "value": "2018-07-01", + "type": "text" + }, + { + "key": "owner_province_state", + "value": "BC", + "type": "text" + }, + { + "key": "owner_mailing_address", + "value": "321 Main Street", + "type": "text" + }, + { + "key": "owner_city", + "value": "Anytown", + "type": "text" + } + ] }, - { - "key": "owner_mailing_address", - "value": "321 Main Street", - "type": "text" + "url": { + "raw": "{{base_url}}/api/v1/submissions/{{filing_number}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "{{filing_number}}" + ] }, - { - "key": "owner_city", - "value": "Anytown", - "type": "text" + "description": "Check if the submission was created correclty." + }, + "response": [] + } + ], + "_postman_isSubFolder": true + }, + { + "name": "Decommission", + "item": [ + { + "name": "Decommission well", + "event": [ + { + "listen": "test", + "script": { + "id": "b89b1daf-40de-46c1-9567-608d995d0702", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 201\", function () {", + " pm.expect(pm.response.code).to.equal(201);", + "})", + "", + "pm.environment.set(\"well_tag_number\", jsonData.well);", + "pm.environment.set('filing_number', jsonData.filing_number)", + "" + ] + } } - ] + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"well_activity_type\": \"DEC\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"ALT_WELL_TEST_CITY\",\n\t\"owner_province_state\": \"BC\",\n\t\"work_start_date\": \"2018-01-01\",\n\t\"work_end_date\": \"2018-01-01\",\n\t\"decommission_description_set\": [\n\t\t{\n\t\t\t\"start\": 0,\n\t\t\t\"end\": 1,\n\t\t\t\"material\": \"CONCRETE\",\n\t\t\t\"observations\": \"It worked!\"\n\t\t}\n\t],\n\t\"decommission_details\": \"Test\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/submissions/decommission?format=json", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "decommission" + ], + "query": [ + { + "key": "format", + "value": "json" + } + ] + }, + "description": "Request a list of wells" + }, + "response": [] }, - "url": { - "raw": "{{base_url}}/api/v1/wells/{{well_tag_number}}", - "host": [ - "{{base_url}}" + { + "name": "Check decommission", + "event": [ + { + "listen": "test", + "script": { + "id": "6071dbc1-9a9a-4806-b276-9ea94186a23c", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "", + "pm.test(\"Expected well owner city to match the submitted alteration report\", function() {", + " pm.expect(jsonData.owner_city).to.equal(\"ALT_WELL_TEST_CITY\");", + "})", + "", + "pm.test(\"Well status is 'closure'\", function() {", + " pm.expect(jsonData.well_status).to.equal(\"CLOSURE\");", + "})" + ] + } + } ], - "path": [ - "api", - "v1", - "wells", - "{{well_tag_number}}" - ] + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "well_activity_type", + "value": "CON", + "type": "text" + }, + { + "key": "driller_responsible", + "value": "{{created_driller_guid}}", + "type": "text" + }, + { + "key": "work_end_date", + "value": "2018-07-02", + "type": "text" + }, + { + "key": "owner_full_name", + "value": "Holly Homeowner", + "type": "text" + }, + { + "key": "work_start_date", + "value": "2018-07-01", + "type": "text" + }, + { + "key": "owner_province_state", + "value": "BC", + "type": "text" + }, + { + "key": "owner_mailing_address", + "value": "321 Main Street", + "type": "text" + }, + { + "key": "owner_city", + "value": "Anytown", + "type": "text" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/v1/wells/{{well_tag_number}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "wells", + "{{well_tag_number}}" + ] + }, + "description": "Check if the created well matched submission" + }, + "response": [] }, - "description": "Check if the created well matched submission" - }, - "response": [] - }, + { + "name": "Check decommission - created decommission submission", + "event": [ + { + "listen": "test", + "script": { + "id": "c0ec586c-6de3-424e-b7cc-67f2b63a32c0", + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.equal(200);", + "})", + "", + "pm.test(\"Expected well owner city to match the submitted alteration report\", function() {", + " pm.expect(jsonData.owner_city).to.equal(\"ALT_WELL_TEST_CITY\");", + "})", + "", + "pm.test(\"Expect linerperforation to be undefined\", function() {", + " pm.expect(jsonData.linerperforation_set).to.equal(undefined);", + "})", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}" + }, + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "well_activity_type", + "value": "CON", + "type": "text" + }, + { + "key": "driller_responsible", + "value": "{{created_driller_guid}}", + "type": "text" + }, + { + "key": "work_end_date", + "value": "2018-07-02", + "type": "text" + }, + { + "key": "owner_full_name", + "value": "Holly Homeowner", + "type": "text" + }, + { + "key": "work_start_date", + "value": "2018-07-01", + "type": "text" + }, + { + "key": "owner_province_state", + "value": "BC", + "type": "text" + }, + { + "key": "owner_mailing_address", + "value": "321 Main Street", + "type": "text" + }, + { + "key": "owner_city", + "value": "Anytown", + "type": "text" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/v1/submissions/{{filing_number}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "v1", + "submissions", + "{{filing_number}}" + ] + }, + "description": "Check if the submission was created correclty." + }, + "response": [] + } + ], + "_postman_isSubFolder": true + } + ] + }, + { + "name": "Stacking", + "item": [ { - "name": "Submission - liner", + "name": "Submission - construction submission", "event": [ { "listen": "test", "script": { - "id": "af2703e6-b88c-41e0-a74f-14fdd2b1f927", + "id": "f43f719e-73c7-4508-a64a-5caeb226bfd5", "type": "text/javascript", "exec": [ "var jsonData = pm.response.json();", @@ -957,6 +1816,8 @@ "pm.test(\"Status code is 201\", function () {", " pm.expect(pm.response.code).to.equal(201);", "})", + "", + "", "" ] } @@ -976,10 +1837,10 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"liner_diameter\": 1,\n\t\"liner_from\": 0,\n\t\"liner_material\": \"PVC\",\n\t\"liner_thickness\": 2,\n\t\"liner_to\": 3,\n\t\"linerperforation_set\": [\n\t\t{\n\t\t\t\"start\": 0,\n\t\t\t\"end\": 10\n\t\t}\n\t]\n}" + "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"casing_set\": [\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"0\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"10\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t}\n\t],\n\t\"screen_set\": [\n {\n \"start\": \"0.00\",\n \"end\": \"10.00\",\n \"internal_diameter\": null,\n \"assembly_type\": null,\n \"slot_size\": null\n }\n ],\n \"linerperforation_set\": [\n\t\t{\n\t\t\t\"start\": 0,\n\t\t\t\"end\": 10\n\t\t}\n\t],\n\t\"well_class\": \"GEOTECH\",\n\t\"driller_name\": \"Bob\",\n\t\"consultant_name\": \"Lois\",\n\t\"consultant_company\": \"Lois Consulting\",\n\t\"work_start_date\": \"2018-01-01\",\n\t\"work_end_date\": \"2018-01-01\",\n\t\"legal_pid\": \"123132\",\n\t\"well_location_description\": \"At the top of the hill\",\n\t\"latitude\": 48.420133,\n\t\"longitude\": -123.370083,\n\t\"ground_elevation\": 1,\n\t\"drilling_method\": \"AUGER\",\n\t\"well_orientation\": \"False\",\n\t\"water_supply_system_name\": \"Pond\",\n\t\"water_supply_system_well_name\": \"1\",\n\t\"surface_seal_material\": \"BNTITE_CLY\",\n\t\"surface_seal_depth\": 1,\n\t\"surface_seal_method\": \"OTHER\",\n\t\"liner_material\": \"PVC\",\n\t\"screen_material\": \"PLASTIC\",\n\t\"development_method\": \"BAIL\",\n\t\"well_yield\": \"5.22\",\n\t\"finished_well_depth\": \"55\",\n\t\"total_depth_drilled\": \"58\",\n\t\"final_casing_stick_up\": \"3\",\n\t\"static_water_level\": \"12\",\n\t\"artesian_flow\": \"6.55\",\n\t\"well_disinfected\": \"True\",\n\t\"comments\": \"This is a well\",\n\t\"diameter\": \"3\"\n}" }, "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", + "raw": "{{base_url}}/api/v1/submissions/construction?format=json", "host": [ "{{base_url}}" ], @@ -987,7 +1848,7 @@ "api", "v1", "submissions", - "" + "construction" ], "query": [ { @@ -1001,19 +1862,24 @@ "response": [] }, { - "name": "Submission - liner empty", + "name": "Submission - alteration", "event": [ { "listen": "test", "script": { - "id": "3737032f-d35f-4597-a306-133d74120ae4", + "id": "6956eea7-3dca-43a2-9357-0a7198a62da4", "type": "text/javascript", "exec": [ "var jsonData = pm.response.json();", "", - "pm.test(\"Status code is 400\", function () {", - " pm.expect(pm.response.code).to.equal(400);", + "pm.test(\"Status code is 201\", function () {", + " pm.expect(pm.response.code).to.equal(201);", + "})", + "", + "pm.test(\"Well tag number to persist\", function() {", + " pm.expect(jsonData.well).to.equal(pm.environment.get('well_tag_number'))", "})", + "", "" ] } @@ -1033,10 +1899,10 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"liner_diameter\": 1,\n\t\"liner_from\": 0,\n\t\"liner_material\": \"PVC\",\n\t\"liner_thickness\": 2,\n\t\"liner_to\": 3,\n\t\"linerperforation_set\": [\n\t\t{\n\t\t}\n\t]\n}" + "raw": "{\n\t\"well_activity_type\": \"ALT\",\n\t\"well\": {{well_tag_number}},\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"work_start_date\": \"2018-01-02\",\n\t\"work_end_date\": \"2018-01-02\",\n\t\"casing_set\": [\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"10\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"20\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t}\n\t],\n\t\"screen_set\": [\n {\n \"start\": \"10.00\",\n \"end\": \"20.00\",\n \"internal_diameter\": null,\n \"assembly_type\": null,\n \"slot_size\": null\n }\n ],\n \"linerperforation_set\": [\n\t\t{\n\t\t\t\"start\": 10,\n\t\t\t\"end\": 20\n\t\t}\n\t]\n}" }, "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", + "raw": "{{base_url}}/api/v1/submissions/alteration?format=json", "host": [ "{{base_url}}" ], @@ -1044,7 +1910,7 @@ "api", "v1", "submissions", - "" + "alteration" ], "query": [ { @@ -1058,12 +1924,12 @@ "response": [] }, { - "name": "Check submission - created well liners", + "name": "Check submission - well updated", "event": [ { "listen": "test", "script": { - "id": "77ba3bf1-a4ad-476e-bd9a-c67bf0e7a34c", + "id": "2f7c420c-60ea-4dc8-bb99-4fa4bd6b59b1", "type": "text/javascript", "exec": [ "var jsonData = pm.response.json();", @@ -1073,171 +1939,20 @@ "})", "", "pm.test(\"Expect casing list to contain two entries\", function() {", - " pm.expect(jsonData.casing_set.length).to.equal(0);", + " pm.expect(jsonData.casing_set.length).to.equal(2);", "})", "", "pm.test(\"Expect screen list empty\", function() {", - " pm.expect(jsonData.screen_set.length).to.equal(0);", + " pm.expect(jsonData.screen_set.length).to.equal(2);", "})", "", "pm.test(\"Expect linerperforation to be empty\", function() {", - " pm.expect(jsonData.linerperforation_set.length).to.equal(1);", - "})" - ] - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" - }, - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ], - "body": { - "mode": "urlencoded", - "urlencoded": [ - { - "key": "well_activity_type", - "value": "CON", - "type": "text" - }, - { - "key": "driller_responsible", - "value": "{{created_driller_guid}}", - "type": "text" - }, - { - "key": "work_end_date", - "value": "2018-07-02", - "type": "text" - }, - { - "key": "owner_full_name", - "value": "Holly Homeowner", - "type": "text" - }, - { - "key": "work_start_date", - "value": "2018-07-01", - "type": "text" - }, - { - "key": "owner_province_state", - "value": "BC", - "type": "text" - }, - { - "key": "owner_mailing_address", - "value": "321 Main Street", - "type": "text" - }, - { - "key": "owner_city", - "value": "Anytown", - "type": "text" - } - ] - }, - "url": { - "raw": "{{base_url}}/api/v1/wells/{{well_tag_number}}", - "host": [ - "{{base_url}}" - ], - "path": [ - "api", - "v1", - "wells", - "{{well_tag_number}}" - ] - }, - "description": "Check if the created well matched submission" - }, - "response": [] - }, - { - "name": "Alteration for unknown well", - "event": [ - { - "listen": "test", - "script": { - "id": "94e81347-a6a8-4f14-825a-bfa24db3099a", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "", - "pm.test(\"Status code is 201\", function () {", - " pm.expect(pm.response.code).to.equal(201);", - "})", - "", - "pm.environment.set(\"well_tag_number\", jsonData.well);", - "" - ] - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"well_activity_type\": \"ALT\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"ALT_WELL_TEST_CITY\",\n\t\"owner_province_state\": \"BC\",\n\t\"casing_set\": [\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"10\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"20\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t}\n\t],\n \"linerperforation_set\": [\n\t\t{\n\t\t\t\"start\": 10,\n\t\t\t\"end\": 20\n\t\t}\n\t]\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", - "host": [ - "{{base_url}}" - ], - "path": [ - "api", - "v1", - "submissions", - "" - ], - "query": [ - { - "key": "format", - "value": "json" - } - ] - }, - "description": "Request a list of wells" - }, - "response": [] - }, - { - "name": "Check submission - Alteration report on unknown well", - "event": [ - { - "listen": "test", - "script": { - "id": "758668aa-93ed-4341-9c01-1eb1c25985dd", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "", - "pm.test(\"Status code is 200\", function () {", - " pm.expect(pm.response.code).to.equal(200);", + " pm.expect(jsonData.linerperforation_set.length).to.equal(2);", "})", "", - "pm.test(\"Expected well owner city to match the submitted alteration report\", function() {", - " pm.expect(jsonData.owner_city).to.equal(\"ALT_WELL_TEST_CITY\");", - "})", - "" + "pm.test(\"Well status is 'NEW'\", function() {", + " pm.expect(jsonData.well_status).to.equal(\"ALTERATION\");", + "})" ] } } @@ -1314,75 +2029,9 @@ "description": "Check if the created well matched submission" }, "response": [] - } - ] - }, - { - "name": "Stacking", - "item": [ - { - "name": "Submission - construction submission", - "event": [ - { - "listen": "test", - "script": { - "id": "f43f719e-73c7-4508-a64a-5caeb226bfd5", - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "", - "pm.environment.set(\"well_tag_number\", jsonData.well);", - "", - "pm.test(\"Status code is 201\", function () {", - " pm.expect(pm.response.code).to.equal(201);", - "})", - "", - "", - "" - ] - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "JWT {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"well_activity_type\": \"CON\",\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"casing_set\": [\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"0\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"10\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t}\n\t],\n\t\"screen_set\": [\n {\n \"start\": \"0.00\",\n \"end\": \"10.00\",\n \"internal_diameter\": null,\n \"assembly_type\": null,\n \"slot_size\": null\n }\n ],\n \"linerperforation_set\": [\n\t\t{\n\t\t\t\"start\": 0,\n\t\t\t\"end\": 10\n\t\t}\n\t]\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", - "host": [ - "{{base_url}}" - ], - "path": [ - "api", - "v1", - "submissions", - "" - ], - "query": [ - { - "key": "format", - "value": "json" - } - ] - }, - "description": "Request a list of wells" - }, - "response": [] }, { - "name": "Submission - alteration", + "name": "Submission - alteration - intersecting data", "event": [ { "listen": "test", @@ -1419,10 +2068,10 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"well_activity_type\": \"ALT\",\n\t\"well\": {{well_tag_number}},\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"casing_set\": [\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"10\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"20\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t}\n\t],\n\t\"screen_set\": [\n {\n \"start\": \"10.00\",\n \"end\": \"20.00\",\n \"internal_diameter\": null,\n \"assembly_type\": null,\n \"slot_size\": null\n }\n ],\n \"linerperforation_set\": [\n\t\t{\n\t\t\t\"start\": 10,\n\t\t\t\"end\": 20\n\t\t}\n\t]\n}" + "raw": "{\n\t\"well_activity_type\": \"ALT\",\n\t\"well\": {{well_tag_number}},\n\t\"work_start_date\": \"2018-01-03\",\n\t\"work_end_date\": \"2018-01-03\",\n\t\"casing_set\": [\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"10\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"15\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t},\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"15\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"20\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t}\n\t],\n\t\"screen_set\": [\n {\n \"start\": \"10.00\",\n \"end\": \"15.00\",\n \"internal_diameter\": null,\n \"assembly_type\": null,\n \"slot_size\": null\n },\n {\n \"start\": \"15.00\",\n \"end\": \"20.00\",\n \"internal_diameter\": null,\n \"assembly_type\": null,\n \"slot_size\": null\n }\n ],\n \"linerperforation_set\": [\n\t\t{\n\t\t\t\"start\": 10,\n\t\t\t\"end\": 15\n\t\t},\n\t\t{\n\t\t\t\"start\": 15,\n\t\t\t\"end\": 20\n\t\t}\n\t]\n}" }, "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", + "raw": "{{base_url}}/api/v1/submissions/alteration?format=json", "host": [ "{{base_url}}" ], @@ -1430,7 +2079,7 @@ "api", "v1", "submissions", - "" + "alteration" ], "query": [ { @@ -1444,12 +2093,12 @@ "response": [] }, { - "name": "Check submission - well updated", + "name": "Check submission - well stacked", "event": [ { "listen": "test", "script": { - "id": "d5ca9d15-a49a-4128-bfdc-cdd37d2e77c4", + "id": "f7640140-774f-4486-ac95-94a38719dd4d", "type": "text/javascript", "exec": [ "var jsonData = pm.response.json();", @@ -1459,15 +2108,15 @@ "})", "", "pm.test(\"Expect casing list to contain two entries\", function() {", - " pm.expect(jsonData.casing_set.length).to.equal(2);", + " pm.expect(jsonData.casing_set.length).to.equal(3);", "})", "", "pm.test(\"Expect screen list empty\", function() {", - " pm.expect(jsonData.screen_set.length).to.equal(2);", + " pm.expect(jsonData.screen_set.length).to.equal(3);", "})", "", "pm.test(\"Expect linerperforation to be empty\", function() {", - " pm.expect(jsonData.linerperforation_set.length).to.equal(2);", + " pm.expect(jsonData.linerperforation_set.length).to.equal(3);", "})" ] } @@ -1542,17 +2191,17 @@ "{{well_tag_number}}" ] }, - "description": "Check if the created well matched submission" + "description": "Check if the created well has stacked data" }, "response": [] }, { - "name": "Submission - alteration - intersecting data", + "name": "Decommission well Copy", "event": [ { "listen": "test", "script": { - "id": "6956eea7-3dca-43a2-9357-0a7198a62da4", + "id": "b89b1daf-40de-46c1-9567-608d995d0702", "type": "text/javascript", "exec": [ "var jsonData = pm.response.json();", @@ -1561,10 +2210,8 @@ " pm.expect(pm.response.code).to.equal(201);", "})", "", - "pm.test(\"Well tag number to persist\", function() {", - " pm.expect(jsonData.well).to.equal(pm.environment.get('well_tag_number'))", - "})", - "", + "pm.environment.set(\"well_tag_number\", jsonData.well);", + "pm.environment.set('filing_number', jsonData.filing_number)", "" ] } @@ -1584,10 +2231,10 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"well_activity_type\": \"ALT\",\n\t\"well\": {{well_tag_number}},\n\t\"casing_set\": [\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"10\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"15\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t},\n\t\t{\n\t\t\t\"casing_code\": \"SURFACE\",\n\t\t\t\"start\":\"15\",\n\t\t\t\"casing_material\":\"STL_PUL_OT\",\n\t\t\t\"end\":\"20\",\n\t\t\t\"diameter\":\"1\",\n\t\t\t\"drive_shoe\":\"False\",\n\t\t\t\"id\":0,\n\t\t\t\"wall_thickness\":\"87\"\n\t\t}\n\t],\n\t\"screen_set\": [\n {\n \"start\": \"10.00\",\n \"end\": \"15.00\",\n \"internal_diameter\": null,\n \"assembly_type\": null,\n \"slot_size\": null\n },\n {\n \"start\": \"15.00\",\n \"end\": \"20.00\",\n \"internal_diameter\": null,\n \"assembly_type\": null,\n \"slot_size\": null\n }\n ],\n \"linerperforation_set\": [\n\t\t{\n\t\t\t\"start\": 10,\n\t\t\t\"end\": 15\n\t\t},\n\t\t{\n\t\t\t\"start\": 15,\n\t\t\t\"end\": 20\n\t\t}\n\t]\n}" + "raw": "{\n\t\"well_activity_type\": \"ALT\",\n\t\"well\": {{well_tag_number}},\n\t\"owner_full_name\": \"Bob\",\n\t\"owner_mailing_address\": \"123 Place\",\n\t\"owner_city\": \"Victoria\",\n\t\"owner_province_state\": \"BC\",\n\t\"work_start_date\": \"2018-01-04\",\n\t\"work_end_date\": \"2018-01-04\",\n\t\"decommission_description_set\": [\n\t\t{\n\t\t\t\"start\": 0,\n\t\t\t\"end\": 1,\n\t\t\t\"material\": \"CONCRETE\",\n\t\t\t\"observations\": \"It worked!\"\n\t\t}\n\t],\n\t\"decommission_details\": \"Test\"\n}" }, "url": { - "raw": "{{base_url}}/api/v1/submissions/?format=json", + "raw": "{{base_url}}/api/v1/submissions/decommission?format=json", "host": [ "{{base_url}}" ], @@ -1595,7 +2242,7 @@ "api", "v1", "submissions", - "" + "decommission" ], "query": [ { @@ -1609,12 +2256,12 @@ "response": [] }, { - "name": "Check submission - well stacked", + "name": "Check well - decommissioned", "event": [ { "listen": "test", "script": { - "id": "f7640140-774f-4486-ac95-94a38719dd4d", + "id": "0cf6450c-2d27-4794-9349-bf1276139cb8", "type": "text/javascript", "exec": [ "var jsonData = pm.response.json();", @@ -1633,6 +2280,15 @@ "", "pm.test(\"Expect linerperforation to be empty\", function() {", " pm.expect(jsonData.linerperforation_set.length).to.equal(3);", + "})", + "", + "pm.test(\"Well status is 'closure'\", function() {", + " pm.expect(jsonData.well_status).to.equal(\"CLOSURE\");", + "})", + "", + "pm.test(\"Latitude/Longitude is same as the initial construction report\", function() {", + " pm.expect(jsonData.latitude).to.equal(\"48.420133\");", + " pm.expect(jsonData.longitude).to.equal(\"-123.370083\");", "})" ] } diff --git a/app/.s2i/bin/assemble b/app/.s2i/bin/assemble index 562612b66d..f909a7dae3 100644 --- a/app/.s2i/bin/assemble +++ b/app/.s2i/bin/assemble @@ -55,10 +55,10 @@ fi cd .. cd frontend npm install -npm run build +# npm run build # Run unit tests in build stage with the code below -#echo "--> testing and building frontend files" -#npm run unit -- --runInBand && npm run build +echo "--> testing and building frontend files" +npm run unit -- --runInBand && npm run build cd .. if should_collectstatic; then @@ -91,9 +91,10 @@ fi # Run unit tests in build stage with the code below #echo "--> running Django unit tests" -#cd backend -#python manage.py test -c nose.cfg -#cd .. +pushd backend +python manage.py test -c nose.cfg +popd backend + # set permissions for any installed artifacts fix-permissions /opt/app-root diff --git a/app/backend/.envrc b/app/backend/.envrc index 57e654da5b..464569fabd 100644 --- a/app/backend/.envrc +++ b/app/backend/.envrc @@ -45,6 +45,7 @@ export REQUIRE_ENV_VARIABLES=True export S3_HOST=s3.ca-central-1.amazonaws.com export S3_ROOT_BUCKET=gwells-docs export SSO_IDP_HINT=idir +export ENABLE_AQUIFERS_SEARCH=True dotenv .secret_env diff --git a/app/backend/aquifers/permissions.py b/app/backend/aquifers/permissions.py new file mode 100644 index 0000000000..6379f30593 --- /dev/null +++ b/app/backend/aquifers/permissions.py @@ -0,0 +1,28 @@ +""" + 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 + + http://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. +""" +from rest_framework.permissions import BasePermission, SAFE_METHODS +from gwells.roles import AQUIFERS_EDIT_ROLE + +class HasAquiferEditRoleOrReadOnly(BasePermission): + """ + Grants permission to users with Aquifer Edit Roles, or is a read-only request + """ + + def has_permission(self, request, view): + return ( + request.method in SAFE_METHODS or + request.user and + request.user.is_authenticated and + request.user.groups.filter(name=AQUIFERS_EDIT_ROLE).exists() + ) diff --git a/app/backend/aquifers/serializers.py b/app/backend/aquifers/serializers.py index 72cd25168e..6a73ffd495 100644 --- a/app/backend/aquifers/serializers.py +++ b/app/backend/aquifers/serializers.py @@ -14,33 +14,113 @@ from rest_framework import serializers -from aquifers.models import Aquifer +from aquifers import models -class AquiferListSerializer(serializers.ModelSerializer): + +class AquiferSerializer(serializers.ModelSerializer): """Serialize a aquifer list""" - demand_description = serializers.SlugRelatedField(source='demand', read_only=True, slug_field='description') - material_description = serializers.SlugRelatedField(source='material', read_only=True, slug_field='description') - productivity_description = serializers.SlugRelatedField(source='productivity', read_only=True, slug_field='description') - subtype_description = serializers.SlugRelatedField(source='subtype', read_only=True, slug_field='description') - vulnerability_description = serializers.SlugRelatedField(source='vulnerability', read_only=True, slug_field='description') + demand_description = serializers.SlugRelatedField( + source='demand', read_only=True, slug_field='description') + material_description = serializers.SlugRelatedField( + source='material', read_only=True, slug_field='description') + productivity_description = serializers.SlugRelatedField( + source='productivity', read_only=True, slug_field='description') + subtype_description = serializers.StringRelatedField( + source='subtype', read_only=True) + vulnerability_description = serializers.SlugRelatedField( + source='vulnerability', read_only=True, slug_field='description') + quality_concern_description = serializers.SlugRelatedField( + source='quality_concern', read_only=True, slug_field='description') + known_water_use_description = serializers.SlugRelatedField( + source='known_water_use', read_only=True, slug_field='description') class Meta: - model = Aquifer + model = models.Aquifer fields = ( 'aquifer_id', 'aquifer_name', 'area', 'demand_description', 'demand', + 'known_water_use_description', + 'known_water_use', 'litho_stratographic_unit', 'location_description', 'mapping_year', 'material_description', 'material', + 'notes', 'productivity_description', 'productivity', + 'quality_concern_description', + 'quality_concern', 'subtype_description', 'subtype', 'vulnerability_description', 'vulnerability' - ) \ No newline at end of file + ) + + +class AquiferMaterialSerializer(serializers.ModelSerializer): + class Meta: + model = models.AquiferMaterial + fields = ( + 'code', + 'description' + ) + + +class QualityConcernSerializer(serializers.ModelSerializer): + class Meta: + model = models.QualityConcern + fields = ( + 'code', + 'description' + ) + + +class AquiferVulnerabilitySerializer(serializers.ModelSerializer): + class Meta: + model = models.AquiferVulnerabilityCode + fields = ( + 'code', + 'description' + ) + + +class AquiferSubtypeSerializer(serializers.ModelSerializer): + description = serializers.CharField(source='__str__') + + class Meta: + model = models.AquiferSubtype + fields = ( + 'code', + 'description' + ) + + +class AquiferProductivitySerializer(serializers.ModelSerializer): + class Meta: + model = models.AquiferProductivity + fields = ( + 'code', + 'description' + ) + + +class AquiferDemandSerializer(serializers.ModelSerializer): + class Meta: + model = models.AquiferDemand + fields = ( + 'code', + 'description' + ) + + +class WaterUseSerializer(serializers.ModelSerializer): + class Meta: + model = models.WaterUse + fields = ( + 'code', + 'description' + ) diff --git a/app/backend/aquifers/templates/aquifers/aquifers.html b/app/backend/aquifers/templates/aquifers/aquifers.html index 0f6db4b906..84b4418a58 100644 --- a/app/backend/aquifers/templates/aquifers/aquifers.html +++ b/app/backend/aquifers/templates/aquifers/aquifers.html @@ -1,7 +1,6 @@ {% extends 'gwells/base_spa.html' %} {% load render_bundle from webpack_loader %} -{% block meta %}Search for registered well drillers and well pump installers who are authorized to work on water wells in B.C. -{% endblock %} +{% block meta %}Search for and access details on over 1,000 mapped aquifers in British Columbia.{% endblock %} {% block title %}Aquifers{% endblock %} {% block body_block %}
diff --git a/app/backend/aquifers/tests.py b/app/backend/aquifers/tests.py index 7ce503c2dd..7902932370 100644 --- a/app/backend/aquifers/tests.py +++ b/app/backend/aquifers/tests.py @@ -1,3 +1,29 @@ from django.test import TestCase +from django.urls import reverse +from django.contrib.auth.models import User + +from rest_framework.test import APITestCase +from rest_framework import status + +from aquifers.models import Aquifer +from gwells.roles import roles_to_groups, AQUIFERS_EDIT_ROLE # Create your tests here. + +class TestPostNotAuthenticated(APITestCase): + def test_not_authenticated_attempts_patch(self): + url = reverse('aquifer-retrieve-update', kwargs={'aquifer_id': 1}) + response = self.client.patch(url, {}, format='json') + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + +class TestAquifersEditRole(APITestCase): + def setUp(self): + user, _created = User.objects.get_or_create(username='test') + roles_to_groups(user, [AQUIFERS_EDIT_ROLE]) + self.client.force_authenticate(user) + Aquifer(aquifer_id=1).save() + + def test_patch(self): + url = reverse('aquifer-retrieve-update', kwargs={'aquifer_id': 1}) + response = self.client.patch(url, {}, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/app/backend/aquifers/urls.py b/app/backend/aquifers/urls.py index 4fb36ae58f..94360a306e 100644 --- a/app/backend/aquifers/urls.py +++ b/app/backend/aquifers/urls.py @@ -21,8 +21,48 @@ url(r'^api/v1/aquifers/$', never_cache(views.AquiferListAPIView.as_view()), name='aquifers-list' - ), + ), + + url(r'^api/v1/aquifers/(?P- Learn more about groundwater wells in B.C.: + Learn more about groundwater wells in B.C.:
Are you sure you would like to save this record?
+Are you sure you want to quit editing this record?
+To update contact information or for general enquiries email groundwater@gov.bc.ca.
- + Learn more about registering as a well driller or well pump installer in B.C.
diff --git a/app/frontend/src/submissions/App.vue b/app/frontend/src/submissions/App.vue index 2de1aaf24e..3086a116b8 100644 --- a/app/frontend/src/submissions/App.vue +++ b/app/frontend/src/submissions/App.vue @@ -11,13 +11,11 @@ diff --git a/app/frontend/src/submissions/components/SubmissionForm/ActivityType.vue b/app/frontend/src/submissions/components/SubmissionForm/ActivityType.vue new file mode 100644 index 0000000000..9e4dd684f9 --- /dev/null +++ b/app/frontend/src/submissions/components/SubmissionForm/ActivityType.vue @@ -0,0 +1,36 @@ + + + + + + + diff --git a/app/frontend/src/submissions/components/SubmissionForm/Casings.vue b/app/frontend/src/submissions/components/SubmissionForm/Casings.vue index a7cac68faa..ce463dd3e8 100644 --- a/app/frontend/src/submissions/components/SubmissionForm/Casings.vue +++ b/app/frontend/src/submissions/components/SubmissionForm/Casings.vue @@ -37,9 +37,9 @@To determine coordinates using a Global Positioning System (GPS), set the datum to North America Datum of 1983 (NAD 83), the current ministry standard for mapping.
-OR
After the GPS coordinates are entered, the map pin can be moved by clicking and dragging it on the map. The GPS coordinates will be updated automatically.
+OR
OR
OR