From 8056e1931b5242b2e6d6cc3c5e5b730021cd06a6 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 09:48:13 -0400 Subject: [PATCH 001/135] update to build --- .github/workflows/github-actions-demo.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 0ca05af..b64f4fd 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -7,5 +7,7 @@ jobs: runs-on: self-hosted steps: - uses: actions/checkout@v2 + - run: cd SportsApp + - run: ./gradlew -DSTAGE_ENVIRONMENT=staging clean build From 2d3a53b0397eb042daa761b521aac9b07eb2a91b Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 09:50:12 -0400 Subject: [PATCH 002/135] fix location --- .github/workflows/github-actions-demo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index b64f4fd..f4455e6 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -7,7 +7,7 @@ jobs: runs-on: self-hosted steps: - uses: actions/checkout@v2 - - run: cd SportsApp + - run: cd SportsApp/Sports - run: ./gradlew -DSTAGE_ENVIRONMENT=staging clean build From 7a48c3cddb639d9a466a2570d308515539b8f3f1 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 09:58:29 -0400 Subject: [PATCH 003/135] updates --- .github/workflows/github-actions-demo.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index f4455e6..fa4689b 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -1,5 +1,5 @@ name: OpenEdgeOps -run-name: ${{ github.actor }} is testing out GitHub Actions 🚀 +run-name: ${{ github.actor }} is compiling our Sample App 🚀 on: [push] jobs: compile: @@ -7,7 +7,8 @@ jobs: runs-on: self-hosted steps: - uses: actions/checkout@v2 - - run: cd SportsApp/Sports - run: ./gradlew -DSTAGE_ENVIRONMENT=staging clean build + working-directory: ./SportsApp/Sports + From 32739281162861a92dc9395e14ec62758939a0a9 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 10:01:59 -0400 Subject: [PATCH 004/135] more updates --- .github/workflows/github-actions-demo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index fa4689b..93aed8c 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -7,7 +7,7 @@ jobs: runs-on: self-hosted steps: - uses: actions/checkout@v2 - - run: ./gradlew -DSTAGE_ENVIRONMENT=staging clean build + - run: sh gradlew -DSTAGE_ENVIRONMENT=staging clean build working-directory: ./SportsApp/Sports From e9967c3e7c30fec61d96fd3e0483addfab587d62 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 10:03:16 -0400 Subject: [PATCH 005/135] removed staging --- .github/workflows/github-actions-demo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 93aed8c..ced5089 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -7,7 +7,7 @@ jobs: runs-on: self-hosted steps: - uses: actions/checkout@v2 - - run: sh gradlew -DSTAGE_ENVIRONMENT=staging clean build + - run: sh gradlew clean build working-directory: ./SportsApp/Sports From 57df5f1eea2a6cacfdcab55460ed759fdb596f69 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 10:11:59 -0400 Subject: [PATCH 006/135] update build --- SportsApp/Sports/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SportsApp/Sports/build.gradle b/SportsApp/Sports/build.gradle index 02d1ed9..946deeb 100644 --- a/SportsApp/Sports/build.gradle +++ b/SportsApp/Sports/build.gradle @@ -67,7 +67,7 @@ task createSports2020(type: CreateDB){ task compileABLApp(type: ABLCompile){ source "AppServer" - propath "AppServer" + propath "AppServer","${dlcHome}/tty/OpenEdge.BusinessLogic.pl" include "**/*.cls" include "**/*.p" rcodeDir = "${buildDir}/rcode/AppServer" From eb28486dfef52065247242b6455a7536137947b6 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 10:32:33 -0400 Subject: [PATCH 007/135] Correcting propath --- SportsApp/Sports/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SportsApp/Sports/build.gradle b/SportsApp/Sports/build.gradle index 946deeb..02d1ed9 100644 --- a/SportsApp/Sports/build.gradle +++ b/SportsApp/Sports/build.gradle @@ -67,7 +67,7 @@ task createSports2020(type: CreateDB){ task compileABLApp(type: ABLCompile){ source "AppServer" - propath "AppServer","${dlcHome}/tty/OpenEdge.BusinessLogic.pl" + propath "AppServer" include "**/*.cls" include "**/*.p" rcodeDir = "${buildDir}/rcode/AppServer" From 023602a369f26be0b1a423a627ea6385132d1e3b Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 10:40:25 -0400 Subject: [PATCH 008/135] Nexus push --- .github/workflows/github-actions-demo.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index ced5089..1f8bf7a 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -8,7 +8,18 @@ jobs: steps: - uses: actions/checkout@v2 - run: sh gradlew clean build - working-directory: ./SportsApp/Sports + working-directory: ./SportsApp/Sports + - name: Nexus Repo Publish + uses: sonatype-nexus-community/nexus-repo-github-action@master + with: + serverUrl: https://ec2-54-80-142-101.compute-1.amazonaws.com:8443 + username: admin + password: Progress2023! + format: maven2 + repository: PugChallengeMaven + coordinates: groupId=com.progress artifactId=sportsapp version=1.0.0 + assets: extension=oear + filename: ./build/distribustion/Sports.oear From a70cfe224ca173835047c3e7ee6eeed436064fbf Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 10:52:21 -0400 Subject: [PATCH 009/135] updated nexus commit --- .github/workflows/github-actions-demo.yml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 1f8bf7a..2a74516 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -9,17 +9,10 @@ jobs: - uses: actions/checkout@v2 - run: sh gradlew clean build working-directory: ./SportsApp/Sports - - name: Nexus Repo Publish - uses: sonatype-nexus-community/nexus-repo-github-action@master - with: - serverUrl: https://ec2-54-80-142-101.compute-1.amazonaws.com:8443 - username: admin - password: Progress2023! - format: maven2 - repository: PugChallengeMaven - coordinates: groupId=com.progress artifactId=sportsapp version=1.0.0 - assets: extension=oear - filename: ./build/distribustion/Sports.oear + - name: upload zip file to nexus + run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distribustion/Sports.oear https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/Sports-1.0.0.oear + + From 8a5cb37a83a45e476ad7dcffc37c1910d03f4839 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 10:55:34 -0400 Subject: [PATCH 010/135] fixed spelling --- .github/workflows/github-actions-demo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 2a74516..cc9414a 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -10,7 +10,7 @@ jobs: - run: sh gradlew clean build working-directory: ./SportsApp/Sports - name: upload zip file to nexus - run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distribustion/Sports.oear https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/Sports-1.0.0.oear + run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/Sports.oear https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/Sports-1.0.0.oear From 52db1066422153ae3fdca291ac2a29092efc9a40 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 10:57:09 -0400 Subject: [PATCH 011/135] added work dir --- .github/workflows/github-actions-demo.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index cc9414a..d1d2efa 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -11,6 +11,7 @@ jobs: working-directory: ./SportsApp/Sports - name: upload zip file to nexus run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/Sports.oear https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/Sports-1.0.0.oear + working-directory: ./SportsApp/Sports From fd81affd907f8550b630d1ad1796a076a9916924 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Wed, 6 Sep 2023 11:15:00 -0400 Subject: [PATCH 012/135] corrected upload --- .github/workflows/github-actions-demo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index d1d2efa..067599b 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -10,7 +10,7 @@ jobs: - run: sh gradlew clean build working-directory: ./SportsApp/Sports - name: upload zip file to nexus - run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/Sports.oear https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/Sports-1.0.0.oear + run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/Sports.oear https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.oear working-directory: ./SportsApp/Sports From ec89484848230728b054f22e1e9f62a8a7ffcd41 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 6 Sep 2023 22:36:01 +0530 Subject: [PATCH 013/135] deploy scripts --- deploy/Dockerfile | 17 + deploy/README.md | 98 + deploy/build.xml | 184 + deploy/conf/runtime.properties | 0 deploy/config.properties | 22 + deploy/lib/ant-contrib-0.6.jar | Bin 0 -> 119512 bytes deploy/scripts/docker-compose/build.xml | 62 + .../docker-compose.template.yml | 56 + deploy/scripts/docker-compose/readme.txt | 13 + deploy/scripts/docker/build.xml | 111 + deploy/scripts/minikube/build.xml | 106 + .../scripts/minikube/deployment.template.yml | 75 + deploy/scripts/minikube/service.template.yml | 17 + deploy/startServer.sh | 104 + deploy/tasks/common-build-tasks.xml | 86 + deploy/webui/grid.js | 76 + deploy/webui/index.html | 63 + deploy/webui/progress.all.js | 14881 ++++++++++++++++ 18 files changed, 15971 insertions(+) create mode 100644 deploy/Dockerfile create mode 100644 deploy/README.md create mode 100644 deploy/build.xml create mode 100644 deploy/conf/runtime.properties create mode 100644 deploy/config.properties create mode 100644 deploy/lib/ant-contrib-0.6.jar create mode 100644 deploy/scripts/docker-compose/build.xml create mode 100644 deploy/scripts/docker-compose/docker-compose.template.yml create mode 100644 deploy/scripts/docker-compose/readme.txt create mode 100644 deploy/scripts/docker/build.xml create mode 100644 deploy/scripts/minikube/build.xml create mode 100644 deploy/scripts/minikube/deployment.template.yml create mode 100644 deploy/scripts/minikube/service.template.yml create mode 100644 deploy/startServer.sh create mode 100644 deploy/tasks/common-build-tasks.xml create mode 100644 deploy/webui/grid.js create mode 100644 deploy/webui/index.html create mode 100644 deploy/webui/progress.all.js diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 0000000..6d5db36 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,17 @@ +FROM alpine:3.8 + +# Root location +ARG ROOT_FOLDER=/deploy-staging + +ARG MANIFEST_VERSION=1.0 + +# Copy archive file +COPY ./output/package-output/instance ${ROOT_FOLDER}/artifacts/ + +# Create "META-INF/MANIFEST.MF" file +RUN mkdir ${ROOT_FOLDER}/META-INF +RUN echo "Manifest-Version: ${MANIFEST_VERSION}" > ${ROOT_FOLDER}/META-INF/MANIFEST.MF +RUN echo "Date-Timestamp: `date +'%Y-%m-%dT%H:%M:%S%z'`" >> ${ROOT_FOLDER}/META-INF/MANIFEST.MF + +# Set working directory +WORKDIR ${ROOT_FOLDER} diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..ddb5fec --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,98 @@ +# Use the Container Image for PAS for OpenEdge 12.6.0 with a Sample Application + +## Requirements: +* Docker environment +* Docker Compose +* A valid progress.cfg file +* OpenEdge 12.6.0 Environment +* Scripts to deploy Progress Application Server for OpenEdge Container. This can be obtained either from the Progress Download Center or Progress communities. + +Note: For the deployment, we are using service port 8811(sample-app), 9200(elasticsearch), 5601(kibana) and 8080(web-ui). These ports should be available. + +## Use the Container Image + +1. Start a database server if you don't have a running database server. Below is an example + * Create a copy of the sports2000 database and start the database server (broker) + * `prodb sports sports2000` + * `proserve sports -S ` + +2. Build the Sports sample app: + * cd Sports + * For connecting to the database, update the ./conf/startup.pf file with the below content and substituting the required field. + * `-db sports -H -S ` + * In an openedge environment, run the below command + * `ant package` + * Note: A Sports.zip file is generated in ./output/package-output + * Change the working directory to the parent to follow further steps + * `cd ..` + +3. Start the Elasticsearch, Kibana and web-ui service + * Update the value for `serviceURI` in `webui/grid.js` to point to your Docker host. + * Increase virtual memory settings to run Elasticsearch: + * `sudo sysctl -w vm.max_map_count=262144` + * For additional info : https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html + * Start the services + * `docker-compose up -d` + * Check the status of Elasticsearch via a web browser. Elasticsearch may take some time to start. + * `http://:9200` + * Check the Elasticsearch starts: + * `docker-compose logs -f elasticsearch` + +4. Deploy the sample app in PASOE + * Download the PASOE image zip from Electronic Software Distribution (ESD) and unzip it to a folder say `pasoe-sample-app`. + For more details, refer https://docs.progress.com/bundle/pas-for-openedge-docker/ + * Change the directory + * `cd pasoe-sample-app` + * Copy the Sports.zip generated from Step 2 to `./deploy/ablapps` + * `cp ../Sports/output/package-output/Sports.zip ./deploy/ablapps/` + * Copy the config.properties from sample app to `./deploy/config.properties` + * `cp ../config.properties ./deploy/config.properties` + * Update the value for `HOST` in `../fluentbit/conf/fluent-bit-output.conf` to point to your Elasticsearch host. + * Copy the Fluent Bit config from sample app to push the logs to Elasticsearch + * `cp ../fluentbit/conf/fluent-bit-output.conf ./deploy/conf/logging/` + * Copy the license file `progress.cfg` to `./deploy/license` + * If you want to change the database during deployment, you can do this by updating the ./deploy/conf/runtime.properties with below content and substituting the requied field. If `localhost` is used in Step 2 for connecting to the database, please do update the ./deploy/conf/runtime.properties to point to the IP_ADDRESS_OF_DB_HOST. + * `Sports.DB.CONNECTION.PARAMS=-db sports -H -S ` + * Deploy the sample app + * `ant -f ./deploy/build.xml deploy` +5. Access the PAS for OpenEdge instance via a web browser: + * `https://:8811/` + * `https://:8811/Sports/` + * `https://:8811/Sports/static/SportsService.json` + * `https://:8811/Sports/rest/SportsService/Customer` + * Note: By default, the PAS for OpenEdge instance will use HTTPS with a test certificate. You will need to accept access with this certificate. + +6. Access the web-ui service via a web browser: + * `http://:8080` + +7. Access Elasticsearch to check on available logs + * `http://:9200/_cat/indices` + +8. Access Kibana + * `http://:5601` + * Notes: + * Select Management/Index Management to see the indices in Elasticsearch.A index named as `pasoe-container-logs` should be present. + * Select Management/Index Pattern to create an index for Kibana: + * Create index patterns as 'pasoe_container*' + * Specify @timestamp to filter data by time + * Select Discover to see pasoe logs. You can search logs for logtype: + * pasoe_agent_log, pasoe_application_log, pasoe_localhost_log, pasoe_localhost_access_log, start_server_log and etc. + +9. Stop the running services + * Stop Elasticsearch, Kibana, and web-ui + * `docker-compose -f ../docker-compose.yaml down` + * Stop deployed sample app + * `ant -f ./deploy/build.xml undeploy` + + + + + +---- Changes ----- +1. Update readme to not point to OE version 12.6.0 +2. Update build.properties to not have hard coded DLC value + a. update the steps in readme to set DLC +3. Use Gradle version 7.3.3 in `progradle` utility + +Steps +1. Set DLC path \ No newline at end of file diff --git a/deploy/build.xml b/deploy/build.xml new file mode 100644 index 0000000..f6bcfbf --- /dev/null +++ b/deploy/build.xml @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Invalid runtime configuration inside ${RUNTIME.PROPERTIES.FILE}: '@{abl.app.name}.DB.CONNECTION.PARAMS=${runtimeProperties.@{abl.app.name}.DB.CONNECTION.PARAMS}'${line.separator}REASON: The ablapp archive is not provided: ${ABL.APP.ARCHIVE} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/deploy/conf/runtime.properties b/deploy/conf/runtime.properties new file mode 100644 index 0000000..e69de29 diff --git a/deploy/config.properties b/deploy/config.properties new file mode 100644 index 0000000..91fa336 --- /dev/null +++ b/deploy/config.properties @@ -0,0 +1,22 @@ +# Deployment mode can be one of: docker/docker-compose/minikube +DEPLOYMENT.MODE=docker-compose + +# Name and tag with which app container image will be built +# Same name will be used as APP_NAME for fluentbit logging +APP.DOCKER.IMAGE.NAME=sports +APP.DOCKER.IMAGE.TAG=latest + +# Container image which contains JDK(compatible) in it +JDK.DOCKER.IMAGE.NAME=eclipse-temurin +JDK.DOCKER.IMAGE.TAG=17.0.3_7-jdk-centos7 +# Location/Path to JDK inside container +JDK.DOCKER.IMAGE.JAVA.LOCATION=/opt/java/openjdk + +PAS.INSTANCE.NAME=oepas1 +PASOE.DOCKER.IMAGE.NAME=progresssoftware/prgs-pasoe +PASOE.DOCKER.IMAGE.TAG=12.8.0 +# In case of kubernetes provide port should be in the default nodePort range: 30000-32767 +PASOE.HTTPS.PORT=8811 + +# Flag to enable fluent-bit logging, defaults to 'true' +FLUENTBIT.LOGGING=true diff --git a/deploy/lib/ant-contrib-0.6.jar b/deploy/lib/ant-contrib-0.6.jar new file mode 100644 index 0000000000000000000000000000000000000000..db90b0aae85e0aba2dc79a75bec216ce678b450c GIT binary patch literal 119512 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deploy/scripts/docker-compose/docker-compose.template.yml b/deploy/scripts/docker-compose/docker-compose.template.yml new file mode 100644 index 0000000..e1be919 --- /dev/null +++ b/deploy/scripts/docker-compose/docker-compose.template.yml @@ -0,0 +1,56 @@ +version: "3.6" +services: + oedbmachine: + image: oedb:latest + ports: + - "7654:7654" + - "7664-7684:7664-7684" + environment: + - DB_BROKER_PORT=7654 + - DB_MINPORT=7664 + - DB_MAXPORT=7684 + web: + image: nginx + ports: + - "8080:80" + volumes: + - ${WEB.UI.DIR}:/usr/share/nginx/html:ro + jdk: + image: ${JDK.DOCKER.IMAGE.FULLNAME} + volumes: + - ${JDK.VOLUME}:${JDK.DOCKER.IMAGE.JAVA.LOCATION} + ablapp: + image: ${APP.DOCKER.IMAGE.FULLNAME} + volumes: + - ${APP.VOLUME}:/deploy-staging/artifacts + pasoeinstance: + image: ${PASOE.DOCKER.IMAGE.FULLNAME} + depends_on: + - jdk + - ablapp + environment: + - FLUENTBIT_LOGGING=${FLUENTBIT.LOGGING} + - APP_NAME=${APP.NAME} + - INSTANCE_NAME=${PAS.INSTANCE.NAME} + ports: + - "${PASOE.HTTPS.PORT}:8811" + container_name: "${PAS.INSTANCE.NAME}_pasoeinstance_dc" + command: ["/bin/sh", "-c", "sh /deploy/scripts/startServer.sh"] + volumes: + - type: volume + source: ${JDK.VOLUME} + target: /usr/java + volume: + nocopy: true + - type: volume + source: ${APP.VOLUME} + target: /deploy/artifacts + volume: + nocopy: true + - ${LICENSE.FILE}:/psc/dlc/progress.cfg + - ${RUNTIME.CONFIG}:/deploy/scripts/config/runtime.properties + - ${LOGGING.CONF.DIR}:/fluentbit-tlr + - /mnt/c/Users/rahulk/work/pug2023/pasoe-docker/deployScripts/deploy/startServer.sh:/deploy/scripts/startServer.sh +volumes: + ${JDK.VOLUME}: + ${APP.VOLUME}: diff --git a/deploy/scripts/docker-compose/readme.txt b/deploy/scripts/docker-compose/readme.txt new file mode 100644 index 0000000..f312e9d --- /dev/null +++ b/deploy/scripts/docker-compose/readme.txt @@ -0,0 +1,13 @@ +## Requirements ## +- Docker Engine version: 18.02.0+ +- JDK image + +## Configuration ## +Provide details of JDK image in the docker-compose.yaml file + +## Deploy ## +- docker-compose build +- docker-compose up -d + +## Undeploy ## +- docker-compose down -v \ No newline at end of file diff --git a/deploy/scripts/docker/build.xml b/deploy/scripts/docker/build.xml new file mode 100644 index 0000000..b44b4eb --- /dev/null +++ b/deploy/scripts/docker/build.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/deploy/scripts/minikube/build.xml b/deploy/scripts/minikube/build.xml new file mode 100644 index 0000000..bd1b252 --- /dev/null +++ b/deploy/scripts/minikube/build.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/deploy/scripts/minikube/deployment.template.yml b/deploy/scripts/minikube/deployment.template.yml new file mode 100644 index 0000000..ab2eaee --- /dev/null +++ b/deploy/scripts/minikube/deployment.template.yml @@ -0,0 +1,75 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ${PAS.INSTANCE.NAME} + labels: + app: ${PAS.INSTANCE.NAME} + version: v1 +spec: + selector: + matchLabels: + app: ${PAS.INSTANCE.NAME} + version: v1 + template: + metadata: + labels: + app: ${PAS.INSTANCE.NAME} + version: v1 + spec: + volumes: + - name: deploy-artifacts-dir + emptyDir: {} + - name: java-dir + emptyDir: {} + - name: license-dir + secret: + secretName: progress-122-license + - name: runtime-config-${PAS.INSTANCE.NAME} + configMap: + name: runtime-config-${PAS.INSTANCE.NAME} + - name: logging-config-${PAS.INSTANCE.NAME} + configMap: + name: logging-config-${PAS.INSTANCE.NAME} + initContainers: + - image: "${APP.DOCKER.IMAGE.FULLNAME}" + name: war + imagePullPolicy: IfNotPresent + command: ["/bin/sh", "-c", "cp -r /deploy-staging/artifacts/* /deploy/artifacts"] + volumeMounts: + - mountPath: /deploy/artifacts + name: deploy-artifacts-dir + - image: "${JDK.DOCKER.IMAGE.FULLNAME}" + name: copy-java + imagePullPolicy: IfNotPresent + command: ["/bin/sh", "-c", "cp -r ${JDK.DOCKER.IMAGE.JAVA.LOCATION}/* /java"] + volumeMounts: + - name: java-dir + mountPath: /java + containers: + - name: ${PAS.INSTANCE.NAME}-container + image: "${PASOE.DOCKER.IMAGE.FULLNAME}" + imagePullPolicy: IfNotPresent + command: ["/bin/sh", "-c", "cp /cfg/progress.cfg /psc/dlc/progress.cfg && sh /deploy/scripts/startServer.sh"] + volumeMounts: + - name: deploy-artifacts-dir + mountPath: /deploy/artifacts + - name: java-dir + mountPath: /usr/java + - name: license-dir + readOnly: false + mountPath: /cfg + - name: runtime-config-${PAS.INSTANCE.NAME} + mountPath: /deploy/scripts/config + - name: logging-config-${PAS.INSTANCE.NAME} + mountPath: /fluentbit-tlr + env: + - name: FLUENTBIT_LOGGING + value: "${FLUENTBIT.LOGGING}" + - name: INSTANCE_NAME + value: ${PAS.INSTANCE.NAME} + - name: APP_NAME + value: ${APP.NAME} + ports: + - name: https + containerPort: 8811 + protocol: TCP diff --git a/deploy/scripts/minikube/service.template.yml b/deploy/scripts/minikube/service.template.yml new file mode 100644 index 0000000..2e71522 --- /dev/null +++ b/deploy/scripts/minikube/service.template.yml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: ${PAS.INSTANCE.NAME}-service + labels: + app: ${PAS.INSTANCE.NAME} + version: v1 +spec: + type: NodePort + ports: + - targetPort: https + nodePort: ${PASOE.HTTPS.PORT} + port: 8811 + protocol: TCP + selector: + app: ${PAS.INSTANCE.NAME} + version: v1 diff --git a/deploy/startServer.sh b/deploy/startServer.sh new file mode 100644 index 0000000..0f61819 --- /dev/null +++ b/deploy/startServer.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +# Start-up script for PAS for OpenEdge (PASOE) Docker container. +# Creates, deploys and starts the PASOE server. +# Starts the FluentBit process, if 'FLUENTBIT_LOGGING=true'. + +# ------------------------------------------------------------------------------------------------ +# Environment variables provided with the Docker image: +# DLC Path to PAS for OpenEdge installation (/psc/dlc) +# +# WRKDIR Path to the Working directory (/psc/wrk) +# +# JAVA_HOME Path to Java Development Kit (JDK) installation (/usr/java) +# +# Environment variables to be provided: +# INSTANCE_NAME PAS for OpenEdge Instance name +# +# APP_NAME (Optional) Aplication name, will be used if FluentBit logging is be enabled. +# This will get logged as a new field in each log entry +# +# FLUENTBIT_LOGGING (Optional) If set to the string 'true', FluentBit logging will be enabled +# Example: FLUENTBIT_LOGGING="true" +# ------------------------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------------------------ +# Artifacts to be provided: +# License Should be provided at DLC location +# (progress.cfg) +# +# Java Development Should be installed at JAVA_HOME location +# Kit (JDK) +# +# .zip Should be provided at '/deploy/artifacts' +# This is the unit of deployment. Refer to the documentation for more details +# +# runtime.properties (Optional) Should be provided at '/deploy/scripts/config' location. +# This can be used to provide runtime configurations. +# Example: .DB.CONNECTION.PARAMS=-db ... +# ------------------------------------------------------------------------------------------------ + +set -e + +## Validations +# Validate if DLC is set +if [ -z ${DLC} ]; then echo "DLC is not set"; exit 1; fi + +# Validate if WRKDIR is set +if [ -z ${WRKDIR} ]; then echo "WRKDIR is not set"; exit 1; fi + +# Validate if license is provided +if [ ! -s ${DLC}/progress.cfg ]; then echo "License is not provided at ${DLC}"; exit 1; fi + +# Validate if JAVA is present +if [ -z ${JAVA_HOME} ]; then echo "JAVA_HOME is not set"; exit 1; +elif [ ! -x "${JAVA_HOME}/bin/java" ]; then echo "JAVA not found at ${JAVA_HOME}"; exit 1; fi + +# Validate if INSTANCE_NAME is set +if [ -z ${INSTANCE_NAME} ]; then echo "INSTANCE_NAME is not set"; exit 1; fi + +# Validate if .zip is provided +if [ ! -s "/deploy/artifacts/${INSTANCE_NAME}.tar.gz" ]; then echo "'${INSTANCE_NAME}.tar.gz' is not provided at '/deploy/artifacts'"; exit 1; fi + +## Create directory for additional log files +LOG_PATH=/psc/wrk/logs +mkdir -p ${LOG_PATH} + +## extract instance archive +cd ${WRKDIR} +mkdir -p tmp +tar -xzf /deploy/artifacts/${INSTANCE_NAME}.tar.gz -C tmp + +## Start PASOE server +${DLC}/bin/pasman create -v -Z pas -u admin:admin ${INSTANCE_NAME} +${INSTANCE_NAME}/bin/tcman.sh import -v tmp/ablapps/Sports.oear +${INSTANCE_NAME}/bin/tcman.sh start + +cd /deploy/scripts +# ant -f ./build.xml -DINSTANCE.ARCHIVE.FILE.ROOT=../artifacts/ deployAndStartInstance -l ${LOG_PATH}/start-server.log + +# SIGTERM is issued by Docker commands (docker stop or docker-compose down) +# Adding a trap for the SIGTERM signal. i.e., to stop PASOE before stopping container +# not_done is a temporary flag +trap 'rm /tmp/not_done; ant -f ./build.xml stopInstance; sleep 10' SIGTERM + +wait_for_sigterm() { + # Loop until not_done flag is removed + touch /tmp/not_done + while [ -f /tmp/not_done ] + do + sleep 60 & + wait $! # This is required such that a trap on main process is triggered + done +} + +## Start FluentBit, if 'FLUENTBIT_LOGGING=true' +if [ "${FLUENTBIT_LOGGING}" = "true" ] +then + export FLUENT_CONFIG_PATH=/etc/fluent-bit + sh /deploy/scripts/setup-fluentbit.sh + /usr/local/bin/fluent-bit -c ${FLUENT_CONFIG_PATH}/fluent-bit.conf -l ${LOG_PATH}/fluent-bit.log & + wait_for_sigterm +else + wait_for_sigterm +fi \ No newline at end of file diff --git a/deploy/tasks/common-build-tasks.xml b/deploy/tasks/common-build-tasks.xml new file mode 100644 index 0000000..3dc48c1 --- /dev/null +++ b/deploy/tasks/common-build-tasks.xml @@ -0,0 +1,86 @@ + + + + + + + + project.setProperty(attributes.get("to"), attributes.get("string").toLowerCase()); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deploy/webui/grid.js b/deploy/webui/grid.js new file mode 100644 index 0000000..04b5078 --- /dev/null +++ b/deploy/webui/grid.js @@ -0,0 +1,76 @@ +/*global alert, $, progress, require */ + +// require("@progress/jsdo"); + +$(function () { + 'use strict'; + var serviceURI = "https://:8811/Sports"; + var catalogURI = serviceURI + "/static/SportsService.json"; + + function createGrid() { + console.log("DEBUG: createGrid(): "); + $('#grid').kendoGrid({ + dataSource: { + serverFiltering: false, + serverSorting: false, + serverPaging: false, + type: "jsdo", + transport: { + jsdo: "Customer" + }, + error: function (e) { + var messages = ""; + if (e.errorThrown) { + messages += e.errorThrown.message + "\n"; + } + e.sender.transport.jsdo.getErrors().forEach(function (err) { + messages += err.error + "\n"; + }); + alert("Error: \n" + messages); + } + }, + selectable: "multiple row", + navigatable: true, + filterable: true, + height: 400, + groupable: true, + reorderable: true, + resizable: true, + sortable: true, + pageable: { + refresh: true, + pageSizes: true, + pageSize: 10, + buttonCount: 5 + }, + editable: 'inline', + toolbar: ['create'], + columns: [ + { + field: 'CustNum', + title: 'Cust Num', + width: 100 + }, + {field: 'Name'}, + {field: 'State'}, + {field: 'Country'}, + {command: ['edit', 'destroy'], title: ' ', width: '250px'} + ] + }); + } + + try { + // Create a new session object + progress.data.getSession({ + serviceURI: serviceURI, + catalogURI: catalogURI, + authenticationModel: "anonymous" + }).then(function (/* jsdosession, result, info */) { + createGrid(); + }, function (/* jsdosession, result, info */) { + alert("Error while creating session."); + }); + } catch (e) { + alert("Error instantiating objects: " + e); + } +}); diff --git a/deploy/webui/index.html b/deploy/webui/index.html new file mode 100644 index 0000000..7eee979 --- /dev/null +++ b/deploy/webui/index.html @@ -0,0 +1,63 @@ + + + + + PUG Challenge 2023! + + + + + + + + + + + + + + +

+

Welcome to PUG Challenge 2023!

+
+ +
+

SportsApp

+

Customer Management

+
+
+ +
+
+

Powered by © Progress Software Corporation

+
+
+ + + \ No newline at end of file diff --git a/deploy/webui/progress.all.js b/deploy/webui/progress.all.js new file mode 100644 index 0000000..5082570 --- /dev/null +++ b/deploy/webui/progress.all.js @@ -0,0 +1,14881 @@ +/* +Progress JSDO Version: 4.4.1 + +Copyright 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. +*/ +/* +progress.util.js Version: 4.4.0-7 + +Copyright (c) 2014-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +Contains support objects used by the jsdo and/or session object + +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. + + */ +/*global progress:true*/ +/*jslint nomen: true*/ +(function () { + + /* Define these if not defined yet - they may already be defined if + * progress.js was included first */ + if (typeof progress === "undefined") { + progress = {}; + } + + if (typeof progress.data === "undefined") { + progress.data = {}; + } + + progress.util = {}; + + var STRING_OBJECT_TYPE = "String", + DATE_OBJECT_TYPE = "Date", + CHARACTER_ABL_TYPE = "CHARACTER"; + + + /** + * Utility class that allows subscribing and unsubscribing from named events. + * + * @returns {progress.util.Observable} + */ + progress.util.Observable = function () { + /* + * Example format of the events object. Some event delegates may only + * have a function setup, others may optionally have scope, and possibly an operation filter + * + * var events = { + * afterfill : [{ + * scope : {}, // this is optional + * fn : function () {}, + * operation : 'getCustomers' // this is optional + * }, ...] + * + * } + * + * + * + */ + + /* + * remove the given function from the array of observers + */ + function _filterObservers(observers, fn, scope, operation) { + return observers.filter(function (el) { + if (el.fn !== fn || el.scope !== scope || el.operation !== operation) { + return el; + } + }, this); + } + + /* + * validate the arguments passed to the subscribe function + */ + this.validateSubscribe = function (args, evt, listenerData) { + + if (args.length >= 2 && (typeof args[0] === 'string') && (typeof args[1] === 'string')) { + listenerData.operation = args[1]; + listenerData.fn = args[2]; + listenerData.scope = args[3]; + + } else if (args.length >= 2 && (typeof args[0] === 'string') && (typeof args[1] === 'function')) { + listenerData.operation = undefined; + listenerData.scope = args[2]; + listenerData.fn = args[1]; + } else { + throw new Error(); + } + + }; + + + /* + * bind the specified function so it receives callbacks when the + * specified event name is called. Event name is not case sensitive. + * An optional scope can be provided so that the function is executed + * in the given scope. If no scope is given, then the function will be + * called without scope. + * + * If the same function is registered for the same event a second time with + * the same scope the original subscription is removed and replaced with the new function + * to be called in the new scope. + * + * This method has two signatures. + * + * Signature 1: + * @param evt The name of the event to bind a handler to. String. Not case sensitive. + * @param fn The function callback for the event . Function. + * @param scope The scope the function is to be run in. Object. Optional. + * + * Signature 2: + * + * @param evt The name of the event to bind a handler to. String. Not case sensitive + * @param operation The name of the operation to bind to. String. Case sensitive. + * @param fn The function callback for the event . Function. + * @param scope The scope the function is to be run in. Object. Optional. + + */ + this.subscribe = function (evt, operation, fn, scope) { + var listenerData, + observers; + + if (!evt) { + throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "subscribe")); + } + + if (typeof evt !== 'string') { + throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), + "subscribe", progress.data._getMsgText("jsdoMSG039"))); + } + + this._events = this._events || {}; + evt = evt.toLowerCase(); + listenerData = {fn: undefined, scope: undefined, operation: undefined}; + + try { + this.validateSubscribe(arguments, evt, listenerData); + } catch (e) { + throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), + "subscribe", e.message)); + } + + observers = this._events[evt] || []; + + // make sure we don't add duplicates + observers = _filterObservers(observers, listenerData.fn, + listenerData.scope, listenerData.operation); + observers.push(listenerData); + this._events[evt] = observers; + + return this; + }; + + /* + * remove the specified function so it no longer receives events from + * the given name. event name is not case sensitive. + * + * This method has two signaturues. + * Signature 1: + * @param evt Required. The name of the event for which to unbind the given function. String. + * @param fn Required. The function to remove from the named event. Function. + * @param scope Optional. The function scope in which to remove the listener. Object. + * + * Signature 2: + * + * @param evt Required. The name of the event for which to unbind the given function. + String. Not case sensitive + * @param operation Required. The name of the operation to receive events. String. Case Sensitive + * @param fn Required. The function to remove from the named event. Function. + * @param scope Optional. The function scope in which to remove the listener. Object. + * + */ + this.unsubscribe = function (evt, operation, fn, scope) { + var listenerData, + observers; + + if (!evt) { + throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "unsubscribe")); + } + + if (typeof evt !== 'string') { + throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), + "unsubscribe", progress.data._getMsgText("jsdoMSG037"))); + } + + this._events = this._events || {}; + evt = evt.toLowerCase(); + listenerData = {fn: undefined, scope: undefined, operation: undefined}; + try { + this.validateSubscribe(arguments, evt, listenerData); + } catch (e) { + // throw new Error("Invalid signature for unsubscribe. " + e.message); + throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), + "unsubscribe", e.message)); + } + + observers = this._events[evt] || []; + if (observers.length > 0) { + this._events[evt] = _filterObservers(observers, listenerData.fn, + listenerData.scope, listenerData.operation); + } + + return this; + }; + + /* + * trigger an event of the given name, and pass the specified data to + * the subscribers of the event. Event name is not case sensitive. + * A variable numbers of arguments can be passed as arguments to the event handler. + * + * This method has two signatures + * Signature 1: + * @param evt The name of the event to fire. String. Not case sensitive. + * @param operation The name of the operation. String. Case sensitive + * @param args Optional. A variable number of arguments to pass to the event handlers. + * + * Signature 2: + * @param evt The name of the event to fire. String. Not case sensitive + * @param args Optional. A variable number of arguments to pass to the event handlers. + */ + this.trigger = function (evt, operation, args) { + var observers, + op; + + if (!evt) { + throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "trigger")); + } + + this._events = this._events || {}; + evt = evt.toLowerCase(); + observers = this._events[evt] || []; + if (observers.length > 0) { + args = Array.prototype.slice.call(arguments); + + if ((arguments.length >= 2) + && (typeof evt === 'string') + && (typeof operation === 'string')) { + // in alt format the second argument is the event name, + // and the first is the operation name + op = operation; + args = args.length > 2 ? args.slice(2) : []; + } else if (arguments.length >= 1 && (typeof evt === 'string')) { + op = undefined; + args = args.length > 1 ? args.slice(1) : []; + } else { + throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), "trigger")); + } + + observers.forEach(function (el) { + if (el.operation === op) { + el.fn.apply(el.scope, args); + } + }); + + } + + return this; + }; + + // unbind all listeners from the given event. If the + // evt is undefined, then all listeners for all events are unbound + // evnt name is not case sensitive + // @param evt Optional. The name of the event to unbind. If not passed, then all events are unbound + this.unsubscribeAll = function (evt, operation) { + var observers; + + if (evt) { + this._events = this._events || {}; + if (typeof evt === 'string') { + evt = evt.toLowerCase(); + observers = this._events[evt] || []; + + observers.forEach(function (el) { + if (el.operation) { + this.unsubscribe(evt, el.operation, el.fn, el.scope); + } else { + this.unsubscribe(evt, el.fn, el.scope); + } + }, this); + } + } else { + this._events = {}; + } + + return this; + }; + }; + + + /** + * Utility class that saves/reads data to localStorage + * + * @returns {progress.data.LocalStorage} + */ + progress.data.LocalStorage = function LocalStorage() { + + /*global localStorage */ + if (typeof localStorage === "undefined") { + // "progress.data.LocalStorage: No support for localStorage." + throw new Error(progress.data._getMsgText("jsdoMSG126", "progress.data.LocalStorage", "localStorage")); + } + + + // "Methods" + + this.saveToLocalStorage = function (name, dataObj) { + localStorage.setItem(name, JSON.stringify(dataObj)); + }; + + this.readFromLocalStorage = function (name) { + + var jsonStr = localStorage.getItem(name), + dataObj = null; + + if (jsonStr !== null) { + try { + dataObj = JSON.parse(jsonStr); + } catch (e) { + dataObj = null; + } + } + return dataObj; + }; + + this.clearLocalStorage = function (name) { + localStorage.removeItem(name); + }; + + }; // End of LocalStorage + + + ///////////////////////////////////////////////////////////////////////////////////////// + // Utility Functions + + /* + * Converts the specified filter object to an OpenEdge ABL Where String. + * + * @param tableRef - handle to the table in jsdo, where string is applied to. + * @param filter - the filter object to convert. + * + * @returns - translated OE where string. + */ + progress.util._convertToABLWhereString = function (tableRef, filter) { + var result = [], + logic = filter.logic || "and", + idx, + length, + field, + fieldInfo, + type, + format, + operator, + value, + ablType, + //filters = (filter.filters) ? filter.filters : [filter], + filters = filter.filters || [filter], + + whereOperators = { + eq: "=", + neq: "<>", + gt: ">", + gte: ">=", + lt: "<", + lte: "<=", + contains : "INDEX", + doesnotcontain: "INDEX", + endswith: "R-INDEX", + startswith: "BEGINS", + isnull: "ISNULL", + isnotnull: "ISNOTNULL", + isempty: "ISEMPTY", + isnotempty: "ISNOTEMPTY" + }; + + for (idx = 0, length = filters.length; idx < length; idx += 1) { + filter = filters[idx]; + field = filter.field; + value = filter.value; + + if (filter.filters) { + filter = progress.util._convertToABLWhereString(tableRef, filter); + } else { + // Use original field name instead of serialized name + if (field && tableRef._name) { + fieldInfo = tableRef._jsdo[tableRef._name]._fields[field.toLowerCase()]; + if (fieldInfo && fieldInfo.origName) { + field = fieldInfo.origName; + } + } + + operator = whereOperators[filter.operator]; + + if (operator === undefined) { + throw new Error("The operator " + filter.operator + " is not valid."); + } + + switch (filter.operator) { + case "isnull": + case "isnotnull": + case "isempty": + case "isnotempty": + value = undefined; + break; + } + + if (operator && value !== undefined) { + type = progress.util._getObjectType(value); + + // We need to build a template format string for the where string. + // We'll first add positional info for the value + if (type === STRING_OBJECT_TYPE) { + format = "'{1}'"; + value = value.replace(/'/g, "~'"); + } else if (type === DATE_OBJECT_TYPE) { + ablType = tableRef._getABLType(field); + if (ablType === "DATE") { + format = "DATE({1:MM, dd, yyyy})"; + } else if (ablType === "DATETIME-TZ") { + // zzz here means to translate timezone offset into minutes + format = "DATETIME-TZ({1:MM, dd, yyyy, hh, mm, ss, fff, zzz})"; + } else { + format = "DATETIME({1:MM, dd, yyyy, hh, mm, ss, fff})"; + } + } else { + format = "{1}"; + } + + // Most where strings are in the format: field operator value. Ex. custnum < 100 + // An exception to this is INDEX() and R-INDEX() which have format: operator field value + // Ex. R-INDEX(name, "LTD") + if (operator === "INDEX" || operator === "R-INDEX") { + if (type !== STRING_OBJECT_TYPE) { + throw new Error("Error parsing filter object. The operator " + filter.operator + + " requires a string value"); + } + if (filter.operator === "doesnotcontain") { + format = "{0}(" + "{2}, " + format + ") = 0"; + } else if (filter.operator === "contains") { + format = "{0}(" + "{2}, " + format + ") > 0"; + } else { // else filter.operator = "endswith" + format = "{2} MATCHES '*{1}'"; + } + } else { + format = "{2} {0} " + format; + } + + filter = progress.util._format(format, operator, value, field); + } else if (operator && value === undefined) { + if (filter.operator === "isempty" || filter.operator === "isnotempty") { + ablType = tableRef._getABLType(field); + if (ablType !== CHARACTER_ABL_TYPE) { + throw new Error("Error parsing filter object. The operator " + filter.operator + + " requires a CHARACTER field"); + } + if (filter.operator === "isempty") { + format = "{2} = ''"; + } else if (filter.operator === "isnotempty") { + format = "{2} <> ''"; + } + } else { + if (filter.operator === "isnull") { + format = "{2} = ?"; + } else if (filter.operator === "isnotnull") { + format = "{2} <> ?"; + } else { + format = "{2} {0} ?"; + } + } + + // format, operator {0}, value {1}, field {2} + filter = progress.util._format(format, operator, value, field); + } + } + + result.push(filter); + } + + filter = result.join(" " + logic + " "); + + if (result.length > 1) { + filter = "(" + filter + ")"; + } + + return filter; + }; + + + /* + * Converts the specified filter object to an SQL Query String. + * + * @param tableName - tableName of table in jsdo, where clause is applied to. + * @param filter - the filter object to convert. + * + * @returns - translated SQL where clause. + */ + progress.util._convertToSQLQueryString = function (tableRef, filter, addSelect) { + var result = [], + logic = filter.logic || "and", + idx, + length, + field, + type, + format, + operator, + value, + fieldFormat, + filters = filter.filters || [filter], + filterStr, + usingLike = true, + + whereOperators = { + eq: "=", + neq: "!=", + gt: ">", + gte: ">=", + lt: "<", + lte: "<=", + contains : "LIKE", + doesnotcontain: "NOT LIKE", + endswith: "LIKE", + startswith: "LIKE", + isnull: "ISNULL", + isnotnull: "ISNOTNULL", + isempty: "ISEMPTY", + isnotempty: "ISNOTEMPTY" + }; + + if (typeof addSelect === "undefined") { + addSelect = false; + } + + for (idx = 0, length = filters.length; idx < length; idx += 1) { + filter = filters[idx]; + field = filter.field; + value = filter.value; + + if (filter.filters) { + filterStr = progress.util._convertToSQLQueryString(tableRef, filter, false); + } else { + operator = whereOperators[filter.operator]; + + if (operator === undefined) { + throw new Error("The operator " + filter.operator + " is not valid."); + } + + switch (filter.operator) { + case "isnull": + case "isnotnull": + case "isempty": + case "isnotempty": + value = undefined; + break; + } + + if (operator && value !== undefined) { + type = progress.util._getObjectType(value); + + if (operator === "LIKE" || operator === "NOT LIKE") { + if (type !== STRING_OBJECT_TYPE) { + throw new Error("Error parsing filter object. The operator " + filter.operator + + " requires a string value"); + } + } + + if (type === STRING_OBJECT_TYPE) { + format = "'{1}'"; + value = value.replace(/'/g, "''"); + } else if (type === DATE_OBJECT_TYPE) { + fieldFormat = tableRef._getFormat(field); + if (fieldFormat === "date") { + format = "'{1:yyyy-MM-dd}'"; + } else if (fieldFormat === "date-time") { + format = "{1:#ISO(iso)}"; + } else if (fieldFormat === "time") { + format = "'{1:FFF}'"; + } + } else { + format = "{1}"; + } + + // We need to build a template format string for the where string. + // We'll first add positional info for the value, which is represented by {1} + if (filter.operator === "startswith") { + format = "'{1}%'"; + } else if (filter.operator === "endswith") { + format = "'%{1}'"; + } else if (filter.operator === "contains" || filter.operator === "doesnotcontain") { + format = "'%{1}%'"; + } else { + usingLike = false; + } + + if (usingLike) { + value = value.replace(/%/g, '\\%'); + value = value.replace(/_/g, '\\_'); + } + + format = "{2} {0} " + format; + filterStr = progress.util._format(format, operator, value, field); + } else if (operator && value === undefined) { + if (filter.operator === "isempty" || filter.operator === "isnotempty") { + type = tableRef._fields[field.toLowerCase()].type; + if (type !== STRING_OBJECT_TYPE.toLowerCase()) { + throw new Error("Error parsing filter object. The operator " + filter.operator + + " requires a string field"); + } + if (filter.operator === "isempty") { + format = "{2} = ''"; + } else if (filter.operator === "isnotempty") { + format = "{2} != ''"; + } + } else { + if (filter.operator === "isnull") { + format = "{2} IS NULL"; + } else if (filter.operator === "isnotnull") { + format = "{2} IS NOT NULL"; + } else { + format = "{2} {0} NULL"; + } + } + + // format, operator {0}, value {1}, field {2} + filterStr = progress.util._format(format, operator, value, field); + } + } + + result.push(filterStr); + } + + filterStr = result.join(" " + logic + " "); + + if (result.length > 1) { + filterStr = "(" + filterStr + ")"; + } + + if (addSelect === true) { + filterStr = "SELECT * FROM " + tableRef._name + " WHERE " + filterStr; + } + + return filterStr; + }; + + + /* + * Returns the object type; Example "String", "Date" + * Constants for object type values are defined above. + * + * @param value - the object whose type is returned + */ + progress.util._getObjectType = function (value) { + // Returns [object xxx]. Removing [object ] + return Object.prototype.toString.call(value).slice(8, -1); + }; + + + /* + * Substitutes in a variable number of arguments into specified format string (with place-holders) + * + * @param fmt - the format string with place-holders, eg. "{0} text {1}". + * + * @returns - formatted string. + */ + progress.util._format = function (fmt) { + /*jslint regexp: true*/ + var values = arguments, + formatRegExp = /\{(\d+)(:[^\}]+)?\}/g; + /*jslint regexp: false*/ + + return fmt.replace(formatRegExp, function (match, index, placeholderFormat) { + var value = values[parseInt(index, 10) + 1]; + + return progress.util._toString(value, placeholderFormat ? placeholderFormat.substring(1) : ""); + }); + + }; + + /* + * Converts the specified value param to a string. + * + * @param value - object to convert + * @param fmt - optional format string with place-holders, eg. "MM dd yyyy". + * + * @returns - converted string. + */ + progress.util._toString = function (value, fmt) { + var str; + + if (fmt) { + if (progress.util._getObjectType(value) === "Date") { + return progress.util._formatDate(value, fmt); + } + } + + if (typeof value === "number") { + str = value.toString(); + } else { + str = (value !== undefined ? value : ""); + } + + return str; + }; + + /* + * Accepts string representing number and optionally pads it with "0"'s to conform to + * specified number of digits. + * + * @param number - string representing number to pad. + * @param digit - number of digits desired for padded string. If not specified, default is 2. + * + * @returns - padded string representing number. + */ + progress.util._pad = function (number, digits) { + var zeros = ["", "0", "00", "000", "0000"], + end; + + number = String(number); + digits = digits || 2; + end = digits - number.length; + + if (end) { + return zeros[digits].substring(0, end) + number; + } + return number; + }; + + /* + * Converts the specified date param to a string. + * + * @param date - date object to convert + * @param fmt - format string with place-holders, eg. "MM dd yyyy". + * + * @returns - converted string. + */ + progress.util._formatDate = function (date, format) { + /*jslint regexp: true*/ + var dateFormatRegExp = + /dd|MM|yyyy|hh|mm|fff|FFF|ss|zzz|iso|"[^"]*"|'[^']*'/g; + /*jslint regexp: false*/ + + return format.replace(dateFormatRegExp, function (match) { + var minutes, + result, + sign; + + if (match === "dd") { + result = progress.util._pad(date.getDate()); + } else if (match === "MM") { + result = progress.util._pad(date.getMonth() + 1); + } else if (match === "yyyy") { + result = progress.util._pad(date.getFullYear(), 4); + } else if (match === "hh") { + result = progress.util._pad(date.getHours()); + } else if (match === "mm") { + result = progress.util._pad(date.getMinutes()); + } else if (match === "ss") { + result = progress.util._pad(date.getSeconds()); + } else if (match === "fff") { + result = progress.util._pad(date.getMilliseconds(), 3); + } else if (match === "FFF") { + result = String(date.getTime()); + } else if (match === "zzz") { + // timezone is returned in minutes + minutes = date.getTimezoneOffset(); + sign = minutes < 0; + result = (sign ? "+" : "-") + minutes; + } else if (match === "iso") { + result = date.toISOString(); + } + + return result !== undefined ? result : match.slice(1, match.length - 1); + }); + }; + + /* + * Processes settings in a jsdoSettings object. + * This method is used by project templates. + */ + progress.util.jsdoSettingsProcessor = function jsdoSettingsProcessor(jsdoSettings) { + if (typeof jsdoSettings === 'object') { + if (jsdoSettings.authenticationModel === undefined || jsdoSettings.authenticationModel === "") { + jsdoSettings.authenticationModel = "ANONYMOUS"; + } + } + }; + +}()); +//# sourceURL=progress.jsdo.js +/* +progress.js Version: 4.4.1-01 + +Copyright (c) 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + // "use strict"; + + var PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS = 20, + PROGRESS_JSDO_OP_STRING = ["none", "create", "read", "update", "delete", "submit"], + PROGRESS_JSDO_ROW_STATE_STRING = ["", "created", "", "modified", "deleted"]; + + /* define these if not defined yet - they may already be defined if + progress.session.js was included first */ + if (typeof progress === 'undefined') { + progress = {}; + } + if (typeof progress.data === 'undefined') { + progress.data = {}; + } + + progress.data._nextid = 0; + progress.data._uidprefix = "" + ( Date.now ? Date.now() : (new Date().getTime())); + + /* 15 - 9 */ + var UID_MAX_VALUE = 999999999999999; + + progress.data._getNextId = function () { + var uid = ++progress.data._nextid; + if (uid >= UID_MAX_VALUE) { + progress.data._nextid = uid = 1; + progress.data._uidprefix = "" + ( Date.now ? Date.now() : (new Date().getTime())); + } + + return progress.data._uidprefix + "-" + uid; + }; + + + var msg = {}; + msg.msgs = {}; +// msg numbers 0 - 99 are related to use of the API (methods and properties we expose to developers) +// 100 - 109 relate to network errors +// 110 - 998 are for miscellaneous errors + + msg.msgs.jsdoMSG000 = "JSDO, Internal Error: {1}"; + msg.msgs.jsdoMSG001 = "JSDO: JSDO has multiple tables. Please use {1} at the table reference level."; + msg.msgs.jsdoMSG002 = "JSDO: Working record for '{1}' is undefined."; + msg.msgs.jsdoMSG003 = "JSDO: {1} function requires a function as a parameter."; + msg.msgs.jsdoMSG004 = "JSDO: Unable to find resource '{1}' in the catalog."; + msg.msgs.jsdoMSG005 = "JSDO: Data for table '{1}' was not specified in addRecords() call."; + msg.msgs.jsdoMSG006 = "JSDO: Data for JSDO was not specified in addRecords() call."; + msg.msgs.jsdoMSG007 = "JSDO: Test function in {1} must return a boolean."; + msg.msgs.jsdoMSG008 = "JSDO: Invalid keyFields parameter in addRecords() call."; + msg.msgs.jsdoMSG009 = "JSDO: KeyField '{1}' in addRecords() call was not found in the schema."; + msg.msgs.jsdoMSG010 = "JSDO: Field '{1}' in relationship was not found in the schema."; + msg.msgs.jsdoMSG011 = "UIHelper: JSDO has multiple tables. " + + "Please use {1} at the table reference level."; + msg.msgs.jsdoMSG012 = "UIHelper: Invalid {2} parameter in {1} call."; + msg.msgs.jsdoMSG020 = "JSDO: tableName parameter must be a string in addRecords() call."; + msg.msgs.jsdoMSG021 = "JSDO: addMode parameter must be specified in addRecords() call."; + msg.msgs.jsdoMSG022 = "JSDO: Invalid addMode specified in addRecords() call."; + msg.msgs.jsdoMSG023 = "JSDO: Duplicate found in addRecords() call using APPEND mode."; + msg.msgs.jsdoMSG024 = "{1}: Unexpected signature in call to {2} function."; + msg.msgs.jsdoMSG025 = "{1}: Invalid parameters in call to {2} function."; + msg.msgs.jsdoMSG026 = "JSDO: saveChanges requires a " + + "CREATE, UPDATE, DELETE or SUBMIT operation to be defined."; + msg.msgs.jsdoMSG030 = "JSDO: Invalid {1}, expected {2}."; + msg.msgs.jsdoMSG031 = "JSDO: Specified sort field name '{1}' was not found in the schema."; + msg.msgs.jsdoMSG032 = "JSDO: Before-image data already exists for record in addRecords() call."; + msg.msgs.jsdoMSG033 = "{1}: Invalid signature for {2}. {3}"; + msg.msgs.jsdoMSG034 = "JSDO: In '{1}' function, JSON data is missing _id"; + msg.msgs.jsdoMSG035 = "JSDO: In '{1}' function, before-image JSON data is missing prods:clientId"; + msg.msgs.jsdoMSG036 = "JSDO: '{1}' can only be called for a dataset"; + msg.msgs.jsdoMSG037 = "{1}: Event name must be provided for {2}."; + msg.msgs.jsdoMSG038 = "Too few arguments. There must be at least {1}."; + msg.msgs.jsdoMSG039 = "The name of the event is not a string."; + msg.msgs.jsdoMSG040 = "The event listener is not a function."; + msg.msgs.jsdoMSG041 = "The event listener scope is not an object."; + msg.msgs.jsdoMSG042 = "'{1}' is not a defined event for this object."; + msg.msgs.jsdoMSG043 = "{1}: A session object was requested to check the status of a Mobile " + + "Service named '{2}', but it has not loaded the definition of that service."; + msg.msgs.jsdoMSG044 = "JSDO: In '{1}' function, {2} is missing {3} property."; + msg.msgs.jsdoMSG045 = "JSDO: {1} function: {2} is missing {3} property."; + msg.msgs.jsdoMSG046 = "JSDO: {1} operation is not defined."; + msg.msgs.jsdoMSG047 = "{1} timeout expired."; + msg.msgs.jsdoMSG048 = "{1}: {2} method has argument '{3}' that is missing property '{4}'."; + msg.msgs.jsdoMSG049 = "{1}: Unexpected error calling {2}: {3}"; + msg.msgs.jsdoMSG050 = "No token returned from server"; + msg.msgs.jsdoMSG051 = "{1} The login method was not executed because the AuthenticationProvider is already logged in."; + msg.msgs.jsdoMSG052 = "{1}: The login method was not executed because no credentials were supplied."; + msg.msgs.jsdoMSG053 = "{1}: {2} was not executed because the AuthenticationProvider is not logged in."; + msg.msgs.jsdoMSG054 = "{1}: Token refresh was not executed because the AuthenticationProvider does not have a refresh token."; + msg.msgs.jsdoMSG055 = "{1}: Token refresh was not executed because the authentication model is not sso."; + msg.msgs.jsdoMSG056 = "{1}: Already logged in."; + msg.msgs.jsdoMSG057 = "{1}: Cannot call {2} when authenticationModel is SSO. Please use the AuthenticationProvider object instead."; + msg.msgs.jsdoMSG058 = "{1}: Cannot pass username and password to addCatalog when authenticationModel " + + "is sso. Pass an AuthenticationProvider instead."; + msg.msgs.jsdoMSG059 = "{1}: Error in constructor. The authenticationModels of the " + + "AuthenticationProvider ({2}) and the JSDOSession ({3}) were not compatible."; + msg.msgs.jsdoMSG060 = "AuthenticationProvider: AuthenticationProvider is no longer logged in. " + + "Tried to refresh SSO token but failed due to authentication error at token server."; + msg.msgs.jsdoMSG061 = "{1}: Attempted to set {2} property to an invalid value."; + + // 100 - 109 relate to network errors + msg.msgs.jsdoMSG100 = "JSDO: Unexpected HTTP response. Too many records."; + msg.msgs.jsdoMSG101 = "Network error while executing HTTP request."; + + // 110 - 499 are for miscellaneous errors + msg.msgs.jsdoMSG110 = "Catalog error: idProperty not specified for resource '{1}'. " + + "idProperty is required {2}."; + msg.msgs.jsdoMSG111 = "Catalog error: Schema '{1}' was not found in catalog."; + msg.msgs.jsdoMSG112 = "Catalog error: Output parameter '{1}' was not found for operation '{2}'."; + msg.msgs.jsdoMSG113 = "Catalog error: Found xType '{1}' for output parameter '{2}' " + + "for operation '{3}' but xType DATASET, TABLE or ARRAY was expected."; + msg.msgs.jsdoMSG114 = "JSDO: idProperty '{1}' is missing from '{2}' record."; + msg.msgs.jsdoMSG115 = "JSDO: Invalid option specified in {1}() call."; + msg.msgs.jsdoMSG116 = "JSDO: {1} parameter must be a string in {2} call."; + msg.msgs.jsdoMSG117 = "JSDO: Schema from storage area '{1}' does not match JSDO schema"; + msg.msgs.jsdoMSG118 = "JSDO: Plugin '{1}' was not found."; + msg.msgs.jsdoMSG119 = "JSDO: A mappingType is expected when 'capabilities' is set." + + " Please specify a plugin (ex: JFP)."; + msg.msgs.jsdoMSG120 = "JSDO: Parameter '{2}' requires capability '{1}' in the catalog."; + msg.msgs.jsdoMSG121 = "{1}: Argument {2} must be of type {3} in {4} call."; + msg.msgs.jsdoMSG122 = "{1}: Incorrect number of arguments in {2} call. There should be {3}."; + msg.msgs.jsdoMSG123 = "{1}: A server response included an invalid '{2}' header."; + msg.msgs.jsdoMSG124 = "JSDO: autoApplyChanges is not supported for saveChanges(true) " + + "with a temp-table. Use jsdo.autoApplyChanges = false."; + msg.msgs.jsdoMSG125 = "{1}: The AuthenticationProvider is not managing valid credentials."; + msg.msgs.jsdoMSG126 = "{1}: No support for {2}."; + msg.msgs.jsdoMSG127 = "JSDO: acceptRowChanges() cannot be called for record with _rejected === true."; + + // 500 - 998 are for generic errors + msg.msgs.jsdoMSG500 = "{1}: '{2}' objects must contain a '{3}' property."; + msg.msgs.jsdoMSG501 = "{1}: '{2}' in '{3}' function cannot be an empty string."; + msg.msgs.jsdoMSG502 = "{1}: The '{2}' parameter passed to the '{3}' function has an invalid value for " + + "its '{4}' property."; + msg.msgs.jsdoMSG503 = "{1}: '{2}' must be of type '{3}'."; + msg.msgs.jsdoMSG504 = "{1}: {2} has an invalid value for the '{3}' property."; + msg.msgs.jsdoMSG505 = "{1}: '{2}' objects must have a '{3}' method."; + // use message below if invalid parameter value is an object + msg.msgs.jsdoMSG506 = "{1}: Invalid argument for the {2} parameter in {3} call."; + // use message below if invalid parameter value is a primitive + msg.msgs.jsdoMSG507 = "{1}: '{2}' is an invalid value for the {3} parameter in {4} call."; + msg.msgs.jsdoMSG508 = "JSDOSession: If a JSDOSession object is using the SSO authentication model, " + + "the options object passed to its constructor must include an authProvider property."; + msg.msgs.jsdoMSG509 = "progress.data.getSession: If the authenticationModel is AUTH_TYPE_SSO, " + + "authenticationURI and authProviderAuthenticationModel are required parameters."; + msg.msgs.jsdoMSG510 = "{1}: This session has been invalidated and cannot be used."; + msg.msgs.jsdoMSG511 = "JSDOSession: addCatalog() can only be called if an AuthenticationProvider was passed as an argument or " + + "connect() has been successfully called."; + + msg.msgs.jsdoMSG998 = "JSDO: JSON object in addRecords() must be DataSet or Temp-Table data."; + + msg.getMsgText = function (n, args) { + var text = msg.msgs[n], + i; + if (!text) { + throw new Error("Message text was not found by getMsgText()"); + } + for (i = 1; i < arguments.length; i += 1) { + text = text.replace(new RegExp('\\{' + i + '\\}', 'g'), arguments[i]); + } + + return text; + }; + + progress.data._getMsgText = msg.getMsgText; + + progress.data.PluginManager = {}; + progress.data.PluginManager._plugins = {}; + + progress.data.PluginManager.addPlugin = function(name, plugin) { + if (progress.data.PluginManager._plugins[name] === undefined) { + progress.data.PluginManager._plugins[name] = plugin; + } + else { + throw new Error("A plugin named '" + name + "' is already registered."); + } + }; + + progress.data.PluginManager.getPlugin = function (name) { + return progress.data.PluginManager._plugins[name]; + }; + + progress.data.JSIndexEntry = function JSIndexEntry(index) { + this.index = index; + }; + + progress.data.JSTableRef = function JSTableRef(jsdo, tableName) { + this._jsdo = jsdo; + this._name = tableName; + this._schema = null; + this._primaryKeys = null; + this._fields = null; + this._processed = {}; + this._visited = false; + + // record is used to represent the current record for a table reference + this.record = null; + + // Data structure + this._data = []; + this._index = {}; + this._hasEmptyBlocks = false; + + // Arrays to keep track of changes + this._beforeImage = {}; + this._added = []; + this._changed = {}; + this._deleted = []; + this._lastErrors = []; + this._convertForServer; + + this._createIndex = function () { + var i, block, id, idProperty; + this._index = {}; + this._hasEmptyBlocks = false; + for (i = 0; i < this._data.length; i += 1) { + block = this._data[i]; + if (!block) { + this._hasEmptyBlocks = true; + continue; + } + id = this._data[i]._id; + if (!id) { + idProperty = this._jsdo._resource.idProperty; + if (typeof(idProperty) == "string") { + id = this._data[i][idProperty]; + if (!id) { + throw new Error(msg.getMsgText("jsdoMSG114", idProperty, this._name)); + } + id += ""; + } + else { + id = progress.data._getNextId(); + } + this._data[i]._id = id; + } + this._index[id] = new progress.data.JSIndexEntry(i); + } + this._needCompaction = false; + }; + + this._compact = function () { + var newDataArray = [], i, block; + + for (i = 0; i < this._data.length; i += 1) { + block = this._data[i]; + if (block) { + newDataArray.push(block); + } + } + this._data = newDataArray; + this._createIndex(); + }; + + this._loadBeforeImageData = function (jsonObject, beforeImageJsonIndex, keyFields) { + var prodsBeforeData = jsonObject[this._jsdo._dataSetName]["prods:before"], + tmpIndex = {}, + record, + record2, + recordId, + key, + tmpKeyIndex, + id, + jsrecord, + tmpDataIndex, + tmpDeletedIndex, + i; + + if (prodsBeforeData && prodsBeforeData[this._name]) { + + if ((Object.keys(this._beforeImage).length !== 0) && keyFields && (keyFields.length !== 0)) { + tmpKeyIndex = {}; + for (id in this._beforeImage) { + jsrecord = this._findById(id, false); + + if (jsrecord) { + key = this._getKey(jsrecord.data, keyFields); + tmpKeyIndex[key] = jsrecord.data; + } + } + } + + for (i = 0; i < prodsBeforeData[this._name].length; i++) { + record = prodsBeforeData[this._name][i]; + tmpIndex[record["prods:id"]] = record; + + if (record["prods:rowState"] == "deleted") { + key = undefined; + + if (keyFields && (keyFields.length !== 0)) { + key = this._getKey(record, keyFields); + } + + if (tmpKeyIndex) { + if (tmpKeyIndex[key] !== undefined) { + throw new Error(msg.getMsgText("jsdoMSG032")); + } + } + + if ((tmpDataIndex === undefined) && keyFields && (keyFields.length !== 0)) { + tmpDataIndex = {}; + tmpDeletedIndex = {}; + + for (var j = 0; j < this._data.length; j++) { + record2 = this._data[j]; + if (!record2) continue; + + var key2 = this._getKey(record2, keyFields); + tmpDataIndex[key2] = record2; + } + + // We also want to check if _deleted record already exists + for (var j = 0; j < this._deleted.length; j++) { + record2 = this._deleted[j].data; + if (!record2) continue; + + var key2 = this._getKey(record2, keyFields); + tmpDeletedIndex[key2] = record2; + } + } + + // First check to see if this deleted record is already in _deleted array + if (key !== undefined) { + record2 = tmpDeletedIndex[key]; + if (record2 !== undefined) { + // If record is already in _deleted array, then nothing more to do here + continue; + } + } + + if (key !== undefined) { + record2 = tmpDataIndex[key]; + if (record2 !== undefined) { + var jsrecord = this._findById(record2._id, false); + if (jsrecord) jsrecord._remove(false); + record._id = record2._id; + } + } + + if (record._id === undefined) + record._id = progress.data._getNextId(); + var copy = {}; + this._jsdo._copyRecord( + this._tableRef, record, copy); + this._jsdo._deleteProdsProperties(copy); + this._beforeImage[record._id] = copy; + var jsrecord = new progress.data.JSRecord(this, copy); + this._deleted.push(jsrecord); + } + } + } + + // Process data using jsonObject instead of _data + // First check if there is after-data for table. Can be called with just before-image data + var tableObject = jsonObject[this._jsdo._dataSetName][this._name]; + if (tableObject) { + for (var i = 0; i < jsonObject[this._jsdo._dataSetName][this._name].length; i++) { + record = jsonObject[this._jsdo._dataSetName][this._name][i]; + recordId = undefined; + if (beforeImageJsonIndex && record["prods:id"]) { + recordId = beforeImageJsonIndex[record["prods:id"]]; + } + switch (record["prods:rowState"]) { + case "created": + if (recordId === undefined) { + recordId = record._id; + } + + // If recordId and record._id are undefined, the record was not processed + if (recordId !== undefined) { + this._beforeImage[recordId] = null; + this._added.push(recordId); + } + break; + case "modified": + var beforeRecord = tmpIndex[record["prods:id"]]; + if (beforeRecord === undefined) { + beforeRecord = {}; + } + + if (recordId === undefined) { + recordId = record._id; + } + // If recordId and record._id are undefined, the record was not processed + if (recordId !== undefined) { + beforeRecord._id = record._id; + + var copy = {}; + this._jsdo._copyRecord( + this._tableRef, beforeRecord, copy); + this._jsdo._deleteProdsProperties(copy); + + this._beforeImage[recordId] = copy; + this._changed[recordId] = record; + + this._beforeImage[beforeRecord._id] = copy; + this._changed[beforeRecord._id] = record; + } + break; + case undefined: + break; // rowState is only specified for records that have changed + default: + throw new Error(msg.getMsgText("jsdoMSG030", + "rowState value in before-image data", "'created' or 'modified'")); + } + } + } + + // Process prods:errors + var prodsErrors = jsonObject[this._jsdo._dataSetName]["prods:errors"]; + if (prodsErrors) { + for (var i = 0; i < prodsErrors[this._name].length; i++) { + var item = prodsErrors[this._name][i]; + var recordId = beforeImageJsonIndex[item["prods:id"]]; + var jsrecord = this._findById(recordId, false); + if (jsrecord) { + jsrecord.data._errorString = item["prods:error"]; + } + } + } + + tmpIndex = null; + }; + + /* + * Clears all data (including any pending changes) in buffer + */ + this._clearData = function () { + this._setRecord(null); + + // Data structure + this._data = []; + this._index = {}; + this._createIndex(); + + // Arrays to keep track of changes + this._beforeImage = {}; + this._added = []; + this._changed = {}; + this._deleted = []; + }; + + this.hasData = function () { + var data; + + // Check if we should return this table with its nested child table's data as nested + if (this._jsdo._nestChildren) { + data = this._getDataWithNestedChildren(this._data); + } + else { + data = this._getRelatedData(); + } + + if (this._hasEmptyBlocks) { + for (var i = 0; i < data.length; i++) { + var block = data[i]; + if (!block) { + return true; + } + } + } + + return data.length !== 0; + }; + + this.getData = function (params) { + var i, + data, + numEmptyBlocks, + newDataArray, + block; + + if (this._needCompaction) { + this._compact(); + } + + if (params && params.filter) { + throw new Error("Not implemented in current version"); + } + // Check if we should return this table with its nested child table's data as nested + else if (this._jsdo._nestChildren) { + data = this._getDataWithNestedChildren(this._data); + } + else { + data = this._getRelatedData(); + } + + if (this._hasEmptyBlocks) { + numEmptyBlocks = 0; + newDataArray = []; + for (i = 0; i < data.length; i += 1) { + block = data[i]; + if (block) { + newDataArray.push(block); + } + else { + numEmptyBlocks++; + } + } + if ((numEmptyBlocks * 100 / this._data.length) >= PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS) + this._needCompaction = true; + + data = newDataArray; + } + else { + // Creates a copy of the data if sort and top are specified + // so that the sorting does not happen in the JSDO memory but + // in a copy of the records + if (params && (params.sort || params.top)) { + newDataArray = []; + for (i = 0; i < data.length; i += 1) { + newDataArray.push(data[i]); + } + data = newDataArray; + } + } + + if (params && (params.sort || params.top)) { + if (params.sort) { + // Converts sort option from Kendo UI to sort option used by the JSDO + sortFields = []; + for (i = 0; i < params.sort.length; i += 1) { + field = params.sort[i].field; + if (params.sort[i].dir == "desc") { + field += ":DESC"; + } + sortFields.push(field); + } + + // Obtain sortObject from sort options to get compare functions + var sortObject = this._processSortFields(sortFields); + if (sortObject.sortFields && sortObject.sortFields.length > 0) { + sortObject.tableRef = this; + data.sort(this._getCompareFn(sortObject)); + } + } + + if (params.top) { + if (typeof(params.skip) == "undefined") { + params.skip = 0; + } + + data = data.splice(params.skip, params.top); + } + } + + return data; + }; + + this._recToDataObject = function (record, includeChildren) { + var array = [record]; + var dataObject = array; + + if (typeof(includeChildren) == 'undefined') { + includeChildren = false; + } + if (this._jsdo._dataSetName) { + dataObject = {}; + dataObject[this._jsdo._dataSetName] = {}; + dataObject[this._jsdo._dataSetName][this._name] = array; + if (includeChildren && this._children.length > 0) { + var jsrecord = this._findById(record._id, false); + if (jsrecord) { + for (var i = 0; i < this._children.length; i++) { + var tableName = this._children[i]; + dataObject[this._jsdo._dataSetName][tableName] = + this._jsdo._buffers[tableName]._getRelatedData(jsrecord); + } + } + } + } + else { + if (this._jsdo._dataProperty) { + dataObject = {}; + dataObject[this._jsdo._dataProperty] = array; + } + } + return dataObject; + }; + + this._recFromDataObject = function (dataObject) { + var data = {}; + if (dataObject) { + if (this._jsdo._dataSetName) { + if (dataObject[this._jsdo._dataSetName]) + data = dataObject[this._jsdo._dataSetName][this._name]; + } + else { + if (this._jsdo._dataProperty) { + if (dataObject[this._jsdo._dataProperty]) + data = dataObject[this._jsdo._dataProperty]; + } + else if (dataObject.data) { + data = dataObject.data; + } + else { + data = dataObject; + } + } + } + + return data instanceof Array ? data[0] : data; + }; + + // Property: schema + this.getSchema = function () { + return this._schema; + }; + this.setSchema = function (schema) { + this._schema = schema; + }; + + // Private method that returns the ABL data type for the specified field + this._getABLType = function (fieldName) { + var i, schema; + + schema = this.getSchema(); + + for (i = 0; i < schema.length; i++) { + if (schema[i].name == fieldName) { + return schema[i].ablType; + } + } + + return undefined; + }; + + // Private method that returns format property (from catalog) for the specified field + this._getFormat = function (fieldName) { + var i, schema; + + schema = this.getSchema(); + + for (i = 0; i < schema.length; i++) { + if (schema[i].name == fieldName) { + return schema[i].format; + } + } + + return undefined; + }; + + + + this.add = function (values) { + return this._add(values, true, true); + }; + + // Alias for add() method + this.create = this.add; + + this._add = function (values, trackChanges, setWorkingRecord) { + if (typeof(trackChanges) == 'undefined') { + trackChanges = true; + } + if (typeof(setWorkingRecord) == 'undefined') { + setWorkingRecord = true; + } + var record = {}, + i, + j, + value, + prefixElement, + name; + + if (typeof values === "undefined") { + values = {}; + } + + // Assign values from the schema + var schema = this.getSchema(); + for (i = 0; i < schema.length; i++) { + var fieldName = schema[i].name; + if (schema[i].type == "array") { + record[fieldName] = []; + if (schema[i].maxItems) { + for (var j = 0; j < schema[i].maxItems; j++) { + record[fieldName][j] = this._jsdo._getDefaultValue(schema[i]); + } + } + + // Assign array values from object parameter + value = values[fieldName]; + if (typeof value != "undefined") { + record[fieldName] = value; + delete values[fieldName]; + } + // Assign values from individual fields from flattened arrays + prefixElement = this._jsdo._getArrayField(fieldName); + if (!record[fieldName]) { + record[fieldName] = []; + } + for (j = 0; j < schema[i].maxItems; j += 1) { + name = prefixElement.name + (j+1); + value = values[name]; + if (typeof value != "undefined") { + if (!this._fields[name.toLowerCase()]) { + // Skip element if a field with the same name exists + // Remove property from object for element since it is not part of the actual schema + delete values[prefixElement.name + (j+1)]; + if (typeof value == 'string' && schema[i].items.type != 'string') { + value = this._jsdo._convertType(value, + schema[i].items.type, + null); + } + record[fieldName][j] = value; + } + } + } + } + else { + record[fieldName] = this._jsdo._getDefaultValue(schema[i]); + } + } + + // Assign values based on a relationship + if (this._jsdo.useRelationships && this._relationship && this._parent) { + if (this._jsdo._buffers[this._parent].record) { + for (var j = 0; j < this._relationship.length; j++) { + record[this._relationship[j].childFieldName] = + this._jsdo._buffers[this._parent].record.data[this._relationship[j].parentFieldName]; + } + } + else + throw new Error(msg.getMsgText("jsdoMSG002", this._parent)); + } + // Assign values from object parameter + for (var v in values) { + record[v] = values[v]; + } + + // Specify _id field - do not use schema default + var id; + var idProperty; + if ((idProperty = this._jsdo._resource.idProperty) !== undefined) { + id = record[idProperty]; + } + if (!id) { + id = progress.data._getNextId(); + } + else { + id += ""; + } + record._id = id; + + if (this.autoSort + && this._sortRecords + && (this._sortFn !== undefined || this._sortObject.sortFields !== undefined)) { + if (this._needsAutoSorting) { + this._data.push(record); + this._sort(); + } + else { + // Find position of new record in _data and use splice + for (var i = 0; i < this._data.length; i++) { + if (this._data[i] === null) continue; // Skip null elements + var ret = this._sortFn ? + this._sortFn(record, this._data[i]) : + this._compareFields(record, this._data[i]); + if (ret == -1) break; + } + this._data.splice(i, 0, record); + } + this._createIndex(); + } + else { + this._data.push(record); + this._index[record._id] = new progress.data.JSIndexEntry(this._data.length - 1); + } + + var jsrecord = new progress.data.JSRecord(this, record); + + // Set record property ignoring relationships + if (setWorkingRecord) + this._setRecord(jsrecord, true); + + if (trackChanges) { + // Save before image + this._beforeImage[record._id] = null; + // End - Save before image + this._added.push(record._id); + } + return jsrecord; + }; + + /* + * Returns records related to the specified jsrecord. + * If jsrecord is not specified the parent working record is used. + */ + this._getRelatedData = function (jsrecord) { + var data = []; + + if (this._data.length === 0) return data; + + if (typeof(jsrecord) == 'undefined') { + if (this._jsdo.useRelationships && this._relationship && this._parent) { + jsrecord = this._jsdo._buffers[this._parent].record; + if (!jsrecord) + throw new Error(msg.getMsgText("jsdoMSG002", this._parent)); + } + } + if (jsrecord) { + // Filter records using relationship + for (var i = 0; i < this._data.length; i++) { + var block = this._data[i]; + if (!block) continue; + + var match = false; + for (var j = 0; j < this._relationship.length; j++) { + match = (jsrecord.data[this._relationship[j].parentFieldName] == + this._data[i][this._relationship[j].childFieldName]); + if (!match) break; + } + if (match) + data.push(this._data[i]); + } + } + else + data = this._data; + + return data; + }; + + + // This method is called on a parent table that has child tables + // where the relationship is specified as NESTED. + // It returns a json array that contains the parent rows. + // If a parent row is involved in nested relationship, + // then references to the child rows are added + // to the parent row in a child table array (providing the nested format) + // We are using the internal jsdo _data arrays, + // and adding a child table array to each parent row that has children. + // Once the caller is done with the nested data, they can call jsdo._unnestData() + // which removes these child table references + this._getDataWithNestedChildren = function (data) { + + // Walk through all the rows and determine if any of its child tables + // should be associated (nested) with the current record + for (var i = 0; i < data.length; i++) { + var parentRecord = data[i]; + + // Now walk thru the parent's children to find any nested children + if (this._children && this._children.length > 0) { + for (var j = 0; j < this._children.length; j++) { + var childBuf = this._jsdo._buffers[this._children[j]]; + + if (childBuf._isNested) { + // If child is nested, then we should walk child records to find matches + for (var k = 0; k < childBuf._data.length; k++) { + var childRecord = childBuf._data[k]; + if (!childRecord) continue; + + var match = false; + for (var m = 0; m < childBuf._relationship.length; m++) { + match = (parentRecord[childBuf._relationship[m].parentFieldName] == + childRecord[childBuf._relationship[m].childFieldName]); + if (!match) break; + } + if (match) { + // Make sure that this parentRecord has an array for its child rows + if (!parentRecord[childBuf._name]) { + parentRecord[childBuf._name] = []; + } + parentRecord[childBuf._name].push(childRecord); + } + + + } // end for; finished adding all child rows for parentRecord + + // The child table may have its own nested children so call recursively + // Use child row array in current parentRecord + if (childBuf._hasNestedChild()) { + childBuf._getDataWithNestedChildren(parentRecord[childBuf._name]); + } + + + } // end if (childBuf._isNested) + } + } + + + } + return data; + + }; + + this._findFirst = function () { + if (this._jsdo.useRelationships && this._relationship && this._parent) { + if (this._jsdo._buffers[this._parent].record) { + // Filter records using relationship + for (var i = 0; i < this._data.length; i++) { + var block = this._data[i]; + if (!block) continue; + + var match = false; + var parentFieldName, childFieldName; + for (var j = 0; j < this._relationship.length; j++) { + parentFieldName = this._relationship[j].parentFieldName; + childFieldName = this._relationship[j].childFieldName; + match = (this._jsdo._buffers[this._parent].record.data[parentFieldName] == + this._data[i][childFieldName]); + if (!match) break; + } + if (match) { + return new progress.data.JSRecord(this, this._data[i]); + } + } + } + } + else { + for (var i = 0; i < this._data.length; i++) { + var block = this._data[i]; + if (!block) continue; + + return new progress.data.JSRecord(this, this._data[i]); + } + } + + + return undefined; + }; + + this._setRecord = function (jsrecord, ignoreRelationships) { + if (jsrecord) { + this.record = jsrecord; + } + else { + this.record = undefined; + } + + // Set child records only if useRelationships is true + if (this._jsdo.useRelationships) { + ignoreRelationships = ((typeof(ignoreRelationships) == 'boolean') && ignoreRelationships); + + if (this._children && this._children.length > 0) { + for (var i = 0; i < this._children.length; i++) { + var childTable = this._jsdo._buffers[this._children[i]]; + if (!ignoreRelationships && this.record && childTable._relationship) { + childTable._setRecord(childTable._findFirst()); + } + else { + childTable._setRecord(undefined, ignoreRelationships); + } + } + } + } + + if (this._jsdo._defaultTableRef) { + this._jsdo.record = this.record; + } + }; + + this.assign = function (values) { + if (this.record) { + return this.record.assign(values); + } + else + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + }; + + // Alias for assign() method + this.update = this.assign; + + this.remove = function () { + if (this.record) { + return this.record._remove(true); + } + else + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + }; + + this._remove = function (bTrackChanges) { + if (this.record) { + return this.record._remove(bTrackChanges); + } + else + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + }; + + this.getId = function () { + if (this.record) { + return this.record.data._id; + } + else + return 0; + }; + + // getErrors() - JSTableRef + this.getErrors = function () { + return this._lastErrors; + }; + + this.getErrorString = function () { + if (this.record) { + return this.record.data._errorString; + } + else + return 0; + }; + + this.findById = function (id) { + return this._findById(id, true); + }; + + this._findById = function (id, setWorkingRecord) { + if (typeof(setWorkingRecord) == 'undefined') { + setWorkingRecord = true; + } + if (id && this._index[id]) { + var record = this._data[this._index[id].index]; + this.record = record ? (new progress.data.JSRecord(this, record)) : null; + if (setWorkingRecord) + this._setRecord(this.record); + return this.record; + } + + if (setWorkingRecord) + this._setRecord(null); + return null; + }; + + /* + * Finds a record in the JSDO memory using the specified function to determine the record. + */ + this.find = function (fn) { + if (typeof(fn) != 'function') { + throw new Error(msg.getMsgText("jsdoMSG003", "find()")); + } + var data = this._getRelatedData(); + + for (var i = 0; i < data.length; i++) { + var block = data[i]; + if (!block) { + continue; + } + this._setRecord(new progress.data.JSRecord(this, data[i])); + var result = fn(this.record); + if (typeof(result) != 'boolean') { + throw new Error(msg.getMsgText("jsdoMSG007", "find()")); + } + if (result) { + return this.record; + } + } + + this._setRecord(null); + return null; + }; + + /* + * Loops through the records + */ + this.foreach = function (fn) { + if (typeof(fn) != 'function') { + throw new Error(msg.getMsgText("jsdoMSG003", "foreach()")); + } + var numEmptyBlocks = 0; + if (this._needCompaction) + this._compact(); + + var data = this._getRelatedData(); + + this._inforeach = true; + for (var i = 0; i < data.length; i++) { + var block = data[i]; + if (!block) { + numEmptyBlocks++; + continue; + } + + this._setRecord(new progress.data.JSRecord(this, data[i])); + var result = fn(this.record); + if ((typeof(result) != 'undefined') && !result) + break; + } + + this._inforeach = false; + + if ((numEmptyBlocks * 100 / this._data.length) >= PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS) + this._needCompaction = true; + }; + + this._equalRecord = function (rec1, rec2, keyFields) { + var field; + var match = true; + for (var i = 0; i < keyFields.length; i++) { + var fieldName = keyFields[i]; + var value1 = rec1[fieldName]; + var value2 = rec2[fieldName]; + + if (!jsdo[tableName].caseSensitive) { + field = jsdo[tableName]._fields[fieldName.toLowerCase()]; + if (field && field.type == "string") { + if (value1 !== undefined && value1 !== null) + value1 = value1.toUpperCase(); + if (value2 !== undefined && value2 !== null) + value2 = value2.toUpperCase(); + } + } + + match = (value1 == value2); + if (!match) return false; + } + return true; + }; + + // Private method to merge changes using merge modes: APPEND, EMPTY, MERGE and REPLACE + this._getKey = function (record, keyFields) { + var keyObject = {}; + for (var i = 0; i < keyFields.length; i++) { + var fieldName = keyFields[i]; + var value = record[fieldName]; + + if (!jsdo[tableName].caseSensitive) { + field = jsdo[tableName]._fields[fieldName.toLowerCase()]; + if (field && field.type == "string") { + if (value !== undefined && value !== null) + value = value.toUpperCase(); + } + } + keyObject[fieldName] = value; + } + return JSON.stringify(keyObject); + }; + + this._getCompareFn = function (sortObject) { + if (typeof sortObject == 'function') { + return function (rec1, rec2) { + if (rec1 === null) return 1; + if (rec2 === null) return -1; + + var jsrec1 = new progress.data.JSRecord(this, rec1); + var jsrec2 = new progress.data.JSRecord(this, rec2); + return sortObject(jsrec1, jsrec2); + }; + } + else return function (rec1, rec2) { + var tableRef = sortObject.tableRef; + var sortFields = sortObject.sortFields; + if (!(sortFields instanceof Array)) return 0; + var sortAscending = sortObject.sortAscending; + + if (rec1 === null) return 1; + if (rec2 === null) return -1; + + var field; + for (var i = 0; i < sortFields.length; i++) { + var fieldName = sortFields[i]; + var value1 = rec1[fieldName]; + var value2 = rec2[fieldName]; + + if (!tableRef.caseSensitive) { + field = tableRef._fields[fieldName.toLowerCase()]; + if (field && field.type == "string") { + if (value1 !== undefined && value1 !== null) + value1 = value1.toUpperCase(); + if (value2 !== undefined && value2 !== null) + value2 = value2.toUpperCase(); + } + } + if (value1 > value2 || (value1 === undefined || value1 === null)) + return sortAscending[i] ? 1 : -1; + else if (value1 < value2 || (value2 === undefined && value2 === null)) + return sortAscending[i] ? -1 : 1; + } + return 0; + }; + }; + + this._sortObject = {}; + this._sortObject.tableRef = this; + this._sortObject.sortFields = undefined; + this._sortObject.sortAscending = undefined; + this._compareFields = this._getCompareFn(this._sortObject); + + // _sortRecords - Tells the table reference whether to sort on add, assign and addRecords + this._sortRecords = true; + // Tells the table reference whether an autoSort is required on an add or assign + this._needsAutoSorting = false; + this._sortFn = undefined; + if ((typeof Object.defineProperty) == 'function') { + this._autoSort = true; + Object.defineProperty( + this, + "autoSort", + { + get: function () { + return this._autoSort; + }, + set: function (value) { + if (value) { + this._autoSort = true; + if (this._sortFn || this._sortObject.sortFields) { + this._sort(); + this._createIndex(); + } + } + else + this._autoSort = false; + }, + enumerable: true, + writeable: true + }); + this._caseSensitive = false; + Object.defineProperty( + this, + "caseSensitive", + { + get: function () { + return this._caseSensitive; + }, + set: function (value) { + if (value) { + this._caseSensitive = true; + } + else + this._caseSensitive = false; + if (this.autoSort && + (this._sortObject.sortFields && !this._sortFn)) { + this._sort(); + this._createIndex(); + } + }, + enumerable: true, + writeable: true + }); + } + else { + this.autoSort = true; + this.caseSensitive = false; // caseSensitive is false by default + } + + this._processSortFields = function (sortFields) { + var sortObject = {}; + if (sortFields instanceof Array) { + sortObject.sortFields = sortFields; + sortObject.sortAscending = []; + sortObject.fields = {}; + for (var i = 0; i < sortObject.sortFields.length; i++) { + var idx; + var fieldName; + var field; + + if (typeof (sortObject.sortFields[i]) != 'string') { + throw new Error(msg.getMsgText("jsdoMSG030", "sort field name", "string element")); + } + if ((idx = sortObject.sortFields[i].indexOf(':')) != -1) { + fieldName = sortObject.sortFields[i].substring(0, idx); + var sortOrder = sortObject.sortFields[i].substring(idx + 1); + switch (sortOrder.toUpperCase()) { + case 'ASCENDING': + case 'ASC': + sortObject.sortAscending[i] = true; + break; + case 'DESCENDING': + case 'DESC': + sortObject.sortAscending[i] = false; + break; + default: + throw new Error(msg.getMsgText("jsdoMSG030", + "sort order '" + sortObject.sortFields[i].substring(idx + 1) + "'", + "ASCENDING or DESCENDING")); + } + } + else { + fieldName = sortObject.sortFields[i]; + sortObject.sortAscending[i] = true; + } + if (fieldName != "_id" && this._fields) { + field = this._fields[fieldName.toLowerCase()]; + if (field) { + if (field.type == "array") + throw new Error(msg.getMsgText("jsdoMSG030", "data type found in sort", + "scalar field")); + fieldName = field.name; + } + else + throw new Error(msg.getMsgText("jsdoMSG031", fieldName)); + } + sortObject.sortFields[i] = fieldName; + sortObject.fields[fieldName] = fieldName; + } + } + else { + sortObject.sortFields = undefined; + sortObject.sortAscending = undefined; + sortObject.fields = undefined; + } + return sortObject; + }; + + this.setSortFields = function (sortFields) { + if (sortFields === undefined || sortFields === null) { + this._sortObject.sortFields = undefined; + this._sortObject.sortAscending = undefined; + } + else if (sortFields instanceof Array) { + var sortObject = this._processSortFields(sortFields); + this._sortObject.sortFields = sortObject.sortFields; + this._sortObject.sortAscending = sortObject.sortAscending; + this._sortObject.fields = sortObject.fields; + + if (this.autoSort) { + this._sort(); + this._createIndex(); + } + } + else + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "setSortFields()")); + }; + + this.setSortFn = function (fn) { + // Check that fn parameter is a function + // Valid values are a function, undefined, or null + // Documentation mentions null as a way to clear the sort function + if (fn && typeof (fn) != 'function') { + throw new Error(msg.getMsgText("jsdoMSG030", "parameter in setSortFn()", + "function parameter")); + } + this._sortFn = fn ? this._getCompareFn(fn) : undefined; + if (this.autoSort) { + this._sort(); + this._createIndex(); + } + }; + + this.sort = function (arg1) { + if (arg1 === undefined || arg1 === null) { + throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "sort()")); + } + if (arguments.length !== 1 || + (!(arg1 instanceof Array) && typeof(arg1) != 'function')) { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "sort()")); + } + + if (arg1 instanceof Array) { + var sortObject = this._processSortFields(arg1); + if (sortObject.sortFields && sortObject.sortFields.length > 0) + this._sort(sortObject); + } + else { + this._sort(arg1); + } + this._createIndex(); + }; + + this._sort = function (arg1) { + if (arguments.length === 0 && + (!this.autoSort || (this._sortFn === undefined && this._sortObject.sortFields === undefined))) + return; + + if (arguments.length === 0) { + if (this._sortFn) { + // Sort using function + this._data.sort(this._sortFn); + } + else { + // Sort using sort fields + this._data.sort(this._compareFields); + } + this._needsAutoSorting = false; + } + else { + if (typeof(arg1) == 'function') { + // Sort using function + this._data.sort(this._getCompareFn(arg1)); + } + else { + // Sort using sort fields + arg1.tableRef = this; + this._data.sort(this._getCompareFn(arg1)); + } + if (this.autoSort) + this._needsAutoSorting = true; + } + }; + + /* + * Reads a JSON object into the JSDO memory for the specified table reference. + */ + this.addRecords = function (jsonObject, addMode, keyFields, trackChanges, isInvoke) { + this._jsdo._addRecords(this._name, jsonObject, addMode, keyFields, trackChanges, isInvoke); + }; + + /* + * Accepts changes for the specified table reference. + */ + this.acceptChanges = function () { + var tableRef = this; + + // First, let's remove any "prods:" properties from created and updated records. + // Don't have to worry about deleted records, since they're going away. + for (var id in tableRef._beforeImage) { + // Create + if (tableRef._beforeImage[id] === null) { + var jsrecord = tableRef._findById(id, false); + if (jsrecord !== null) { + tableRef._jsdo._deleteProdsProperties(jsrecord.data, true); + } + + } + // Update + else if (this._changed[id] !== undefined) { + var jsrecord = this._findById(id, false); + if (jsrecord !== null) { + tableRef._jsdo._deleteProdsProperties(jsrecord.data, true); + } + } + } + + tableRef._processed = {}; + tableRef._added = []; + tableRef._changed = {}; + tableRef._deleted = []; + tableRef._beforeImage = {}; + }; + + /* + * Rejects changes for the specified table reference. + */ + this.rejectChanges = function () { + // Reject changes + for (var id in this._beforeImage) { + if (this._beforeImage[id] === null) { + // Undo create + this._jsdo._undoCreate(this, id); + } + else if (this._changed[id] !== undefined) { + // Undo update + this._jsdo._undoUpdate(this, id, true); + } + else { + // Undo delete + this._jsdo._undoDelete(this, id, true); + } + } + + var tableRef = this; + tableRef._processed = {}; + tableRef._added = []; + tableRef._changed = {}; + tableRef._deleted = []; + }; + + this.hasChanges = function () { + return (Object.keys(this._beforeImage).length !== 0); + }; + + this.getChanges = function () { + var result = []; + for (var id in this._beforeImage) { + var item = {rowState: "", record: null}; + // Create + if (this._beforeImage[id] === null) { + item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_CREATE]; + item.record = this._findById(id, false); + } + // Update + else if (this._changed[id] !== undefined) { + item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_UPDATE]; + item.record = this._findById(id, false); + } + // Delete + else { + item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_DELETE]; + item.record = new progress.data.JSRecord(this, this._beforeImage[id]); + } + result.push(item); + } + return result; + }; + + /* + * Private method to apply changes for the specified table reference. + * If _errorString has been set for a row, row change is rejected. + * If it has not been set, acceptRowChanges() is called. + */ + this._applyChanges = function () { + for (var id in this._beforeImage) { + // Create + if (this._beforeImage[id] === null) { + var jsrecord = this._findById(id, false); + if (jsrecord !== null) { + if (jsrecord.data._rejected + || (jsrecord.data._errorString !== undefined)) { + this._jsdo._undoCreate(this, id); + } + else { + jsrecord.acceptRowChanges(); + } + } + else { + // Record not present in JSDO memory + // Delete after Create + var found = false; + for (var i = 0; i < this._deleted.length; i++) { + found = (this._deleted[i].data._id == id); + if (found) break; + } + if (!found) { + throw new Error(msg.getMsgText("jsdoMSG000", + "Created record appears to be deleted without a delete operation.")); + } + } + } + // Update + else if (this._changed[id] !== undefined) { + var jsrecord = this._findById(id, false); + if (jsrecord !== null) { + // Record found in JSDO memory + if (jsrecord.data._rejected + || (jsrecord.data._errorString !== undefined)) { + this._jsdo._undoUpdate(this, id); + } + else { + jsrecord.acceptRowChanges(); + } + } + else { + // Record not present in JSDO memory + // Delete after Update + if (this._beforeImage[id]._rejected + || (this._beforeImage[id]._errorString !== undefined)) { + this._jsdo._undoDelete(this, id); + } + else { + var found = false; + for (var i = 0; i < this._deleted.length; i++) { + found = (this._deleted[i].data._id == id); + if (found) break; + } + if (!found) { + throw new Error(msg.getMsgText("jsdoMSG000", + "Updated record appears to be deleted without a delete operation.")); + } + } + } + } + // Delete + else { + if (this._beforeImage[id]._rejected + || (this._beforeImage[id]._errorString !== undefined)) { + this._jsdo._undoDelete(this, id); + } + } + } + + var tableRef = this; + tableRef._processed = {}; + tableRef._added = []; + tableRef._changed = {}; + tableRef._deleted = []; + tableRef._beforeImage = {}; + }; + + + /* + * Accepts row changes for the working record at the table reference level. + */ + this.acceptRowChanges = function () { + if (this.record) + return this.record.acceptRowChanges(); + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + }; + + /* + * Rejects row changes for the working record at the table reference level. + */ + this.rejectRowChanges = function () { + if (this.record) + return this.record.rejectRowChanges(); + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + }; + + + /* This method returns true + * if this table has any child tables and at least one of those tables is nested. + * Else if returns false. + */ + this._hasNestedChild = function () { + var hasNestedChild = false; + var childBufObj; + + // If table has children, see if any relationship is NESTED + if (this._children.length > 0) { + for (var i = 0; i < this._children.length; i++) { + childBufObj = this._jsdo._buffers[this._children[i]]; + + if (childBufObj._isNested) { + hasNestedChild = true; + break; + } + } + } + + return hasNestedChild; + }; + }; + + /* + * Returns a JSRecord for the specified JSDO. + * @param jsdo the JSDO + * @param record the values of the record + */ + progress.data.JSRecord = function JSRecord(tableRef, record) { + this._tableRef = tableRef; + this.data = record; + + this.getId = function () { + return this.data._id ? this.data._id : null; + }; + + this.getErrorString = function () { + return this.data._errorString; + }; + + /* + * Saves a copy of the current record to the before image. + */ + this._saveBeforeImageUpdate = function () { + // Save before image + if (this._tableRef._beforeImage[this.data._id] === undefined) { + // this.data._index = index; + var copy = {}; + this._tableRef._jsdo._copyRecord( + this._tableRef, this.data, copy); + this._tableRef._beforeImage[this.data._id] = copy; + } + + if (this._tableRef._changed[this.data._id] === undefined) { + this._tableRef._changed[this.data._id] = this.data; + } + // End - Save before image + }; + + /* + * + */ + this._sortRecord = function (fields) { + var index = this._tableRef._index[this.data._id].index; + var record = this._tableRef._data[index]; + + if (this._tableRef.autoSort + && this._tableRef._sortRecords + && (this._tableRef._sortFn !== undefined + || this._tableRef._sortObject.sortFields !== undefined)) { + + if (this._tableRef._sortObject.fields) { + if (typeof fields == 'string') { + if (this._tableRef._sortObject.fields[fields] === undefined) + return; // Only sort records if the the specified field is in the sort fields + } + else if (fields instanceof Array) { + var found = false; + for (var i = 0; i < fields.length; i++) { + if (this._tableRef._sortObject.fields[fields[i]] !== undefined) { + found = true; + break; + } + } + if (!found) + return; // Only sort records if the the specified fields are in the sort fields + } + } + + if (this._tableRef._needsAutoSorting) { + this._tableRef._sort(); + this._tableRef._createIndex(); + } + else { + // Find position of new record in _data and use splice + for (var i = 0; i < this._tableRef._data.length; i++) { + if (this._tableRef._data[i] === null) continue; // Skip null elements + if (i == index) continue; // Skip changed record + var ret = this._tableRef._sortFn ? + this._tableRef._sortFn(record, this._tableRef._data[i]) : + this._tableRef._compareFields(record, this._tableRef._data[i]); + if (ret == -1) break; + } + + if (i > index) { + i--; + } + if (i != index) { + this._tableRef._data.splice(index, 1); + this._tableRef._data.splice(i, 0, record); + this._tableRef._createIndex(); + } + } + } + }; + + /* + * Assigns the specified values. + * @param record parameter with the record values + */ + this.assign = function (record) { + if (record === undefined) + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "assign() or update()")); + + this._saveBeforeImageUpdate(); + + var fieldName, + i, + j, + value, + schema = this._tableRef.getSchema(), + prefixElement, + name; + + if (record) { + for (i = 0; i < schema.length; i += 1) { + fieldName = schema[i].name; + value = record[fieldName]; + if (typeof value != "undefined") { + if (typeof value == 'string' && schema[i].type != 'string') { + value = this._tableRef._jsdo._convertType(value, + schema[i].type, + schema[i].items ? schema[i].items.type : null); + } + this.data[fieldName] = value; + } + if (schema[i].type === "array") { + // Assign values from individual fields from flattened arrays + prefixElement = this._tableRef._jsdo._getArrayField(fieldName); + if (!this.data[fieldName]) { + this.data[fieldName] = []; + } + for (j = 0; j < schema[i].maxItems; j += 1) { + name = prefixElement.name + (j+1); + value = record[name]; + if (typeof value != "undefined") { + // Skip element if a field with the same name exists + if (!this._tableRef._fields[name.toLowerCase()]) { + if (typeof value == 'string' && schema[i].items.type != 'string') { + value = this._tableRef._jsdo._convertType(value, + schema[i].items.type, + null); + } + this.data[fieldName][j] = value; + } + } + } + } + } + + this._sortRecord(); + } + return true; + }; + + // Alias for assign() method + this.update = this.assign; + + /* + * Removes the JSRecord. + */ + this.remove = function () { + return this._remove(true); + }; + + this._remove = function (bTrackChanges) { + if (typeof(bTrackChanges) == 'undefined') { + bTrackChanges = true; + } + + var index = this._tableRef._index[this.data._id].index; + var jsrecord = this._tableRef._findById(this.data._id, false); + + if (bTrackChanges) { + // Save before image + var record = this._tableRef._beforeImage[this.data._id]; + if (record === undefined) { + // Record does not exist in the before image + this.data._index = index; + this._tableRef._beforeImage[this.data._id] = this.data; + } + else { + // Record exists in the before image + if (record) { + // Record is not null - a null entry in the before image indicates + // corresponds to an add + // Save the index of the record + // so that an undo would restore the record in the same position in _data + record._index = index; + } + } + // End - Save before image + this._tableRef._deleted.push(jsrecord); + } + + // Set entry to null instead of removing entry - index requires positions to be persistent + this._tableRef._data[index] = null; + this._tableRef._hasEmptyBlocks = true; + delete this._tableRef._index[this.data._id]; + + // Set record property + this._tableRef._setRecord(null); + + return true; + }; + + /* + * Accepts row changes for the specified record. + */ + this.acceptRowChanges = function () { + var id = this.data._id; + if (this._tableRef._beforeImage[id] !== undefined) { + if (this.data._rejected) { + throw new Error(msg.getMsgText("jsdoMSG127")); + } + if (this._tableRef._beforeImage[id] === null) { + // Accept create + // Remove element from _added + for (var i = 0; i < this._tableRef._added.length; i++) { + if (this._tableRef._added[i] == id) { + this._tableRef._added.splice(i, 1); + break; + } + } + this._tableRef._jsdo._deleteProdsProperties(this.data, true); + } + else if (this._tableRef._changed[id] !== undefined) { + // Accept update + delete this._tableRef._changed[id]; + this._tableRef._jsdo._deleteProdsProperties(this.data, true); + } + else { + // Accept delete + // Remove element from _deleted + for (var i = 0; i < this._tableRef._deleted.length; i++) { + if (this._tableRef._deleted[i].data._id == id) { + this._tableRef._deleted.splice(i, 1); + break; + } + } + } + delete tableRef._beforeImage[id]; + } + }; + + /* + * Rejects row changes for the specified record. + */ + this.rejectRowChanges = function () { + var id = this.data._id; + if (this._tableRef._beforeImage[id] !== undefined) { + if (this._tableRef._beforeImage[id] === null) { + // Undo create + this._tableRef._jsdo._undoCreate(this._tableRef, id); + // Remove element from _added + for (var i = 0; i < this._tableRef._added.length; i++) { + if (this._tableRef._added[i] == id) { + this._tableRef._added.splice(i, 1); + break; + } + } + } + else if (this._tableRef._changed[id] !== undefined) { + // Undo update + this._tableRef._jsdo._undoUpdate(this._tableRef, id, true); + delete this._tableRef._changed[id]; + } + else { + // Undo delete + this._tableRef._jsdo._undoDelete(this._tableRef, id, true); + // Remove element from _deleted + for (var i = 0; i < this._tableRef._deleted.length; i++) { + if (this._tableRef._deleted[i].data._id == id) { + this._tableRef._deleted.splice(i, 1); + break; + } + } + } + delete tableRef._beforeImage[id]; + } + }; + + }; + + /* + * Returns a JSDO for the specified resource. + * @param resNameOrParmObj: the resource name or an object that contains the initial values for the JSDO + * (if this is an object, it should include the name property with the resource name + * @param serviceName : name of service (ignored if 1st param is an object containing the initial values) + */ + progress.data.JSDO = function JSDO(resNameOrParmObj, serviceName) { + var _super = {}; + + if (typeof progress.data.Session == 'undefined') { + throw new Error('ERROR: You must include progress.session.js'); + } + + _super.subscribe = this.subscribe; + + // Override for Observable.subscribe + this.subscribe = function(evt) { + var args = Array.prototype.slice.call(arguments); + if (typeof evt === "string") { + // Aliases for events + switch(evt.toLowerCase()) { + case "beforeread": + args[0] = "beforefill"; + break; + case "afterread": + args[0] = "afterfill"; + break; + } + } + _super.subscribe.apply(this, args); + }; + + this._defineProperty = function (tableName, fieldName) { + Object.defineProperty( + this._buffers[tableName], + fieldName, + { + get: function fnGet() { + var name, + index, + element, + fieldInfo; + if (this.record) { + index = fieldName.indexOf(progress.data.JSDO.ARRAY_INDEX_SEPARATOR); + if (index > 0 && !this._fields[fieldName.toLowerCase()]) { + // Skip element if a field with the same name exists + // Check if field is a flattened array field by quickly checking for the separator + // Extract name and index element + name = fieldName.substring(0, index); + element = fieldName.substring(index + progress.data.JSDO.ARRAY_INDEX_SEPARATOR.length); + fieldInfo = this._fields[name.toLowerCase()]; + if (!isNaN(element) && fieldInfo && (fieldInfo.type === "array")) { + return this.record.data[name][element - 1]; + } + } + return this.record.data[fieldName]; + } + else + return null; + }, + set: function (value) { + var name = fieldName, + index, + element, + fieldInfo; + if (this.record) { + this.record._saveBeforeImageUpdate(); + + try { + index = fieldName.indexOf(progress.data.JSDO.ARRAY_INDEX_SEPARATOR); + if (index > 0 && !this._fields[fieldName.toLowerCase()]) { + // Skip element if a field with the same name exists + name = fieldName.substring(0, index); + element = fieldName.substring(index + progress.data.JSDO.ARRAY_INDEX_SEPARATOR.length); + fieldInfo = this._fields[name.toLowerCase()]; + if (!isNaN(element) && fieldInfo && (fieldInfo.type === "array")) { + this.record.data[name][element - 1] = value; + return; + } + } + this.record.data[fieldName] = value; + } + finally { + this.record._sortRecord(name); + } + } + }, + enumerable: true, + writeable: true + }); + }; + + // Initial values + this._buffers = {}; // Object of table references + this._numBuffers = 0; + this._defaultTableRef = null; + + this._async = true; + this._dataProperty = null; + this._dataSetName = null; + this.operations = []; + this.useRelationships = true; + + this._session = null; + this._needCompaction = false; + + this._hasCUDOperations = false; + this._hasSubmitOperation = false; + this._useSubmit = false; // For saving saveChanges(useSubmit) param + + this.autoApplyChanges = true; // default should be true to support 11.2 behavior + this._lastErrors = []; + this._localStorage = null; + this._convertForServer; + var autoFill = false; + + // Initialize JSDO using init values + if (!arguments[0]) { + throw new Error("JSDO: Parameters are required in constructor."); + } + + if (typeof(arguments[0]) == "string") { + this.name = arguments[0]; +// if ( arguments[1] && (typeof(arguments[1]) == "string") ) +// localServiceName = serviceName; + } + else if (typeof(arguments[0]) == "object") { + var args = arguments[0]; + for (var v in args) { + switch (v) { + case 'autoFill': + autoFill = args[v]; + break; + case 'events': + this._events = {}; + for (var eventName in args[v]) { + this._events[eventName.toLowerCase()] = args[v][eventName]; + } + break; + case 'dataProperty': + this._dataProperty = args[v]; + break; + default: + this[v] = args[v]; + } + } + } + /* error out if caller didn't pass the resource name */ + if ((!this.name) /*|| !(this._session)*/) { + // make this error message more specific? + throw new Error("JSDO: JSDO constructor is missing the value for 'name'"); + } + + /* perform some basic validation on the event object for the proper structure if provided */ + if (this._events) { + if ((typeof this._events) !== 'object') { + throw new Error("JSDO: JSDO constructor event object is not defined as an object"); + } + + /* make sure all the event handlers are sane */ + for (var prop in this._events) { + var evt = this._events[prop]; + if (!(evt instanceof Array)) { + throw new Error('JSDO: JSDO constructor event object for ' + prop + ' must be an array'); + } + evt.forEach(function (el) { + if ((typeof el) !== 'object') { + throw new Error("JSDO: JSDO constuctor event object for " + + prop + " is not defined as an object"); + } + /* listener must have at least fn property defined as a function */ + if ((typeof el.fn) !== 'function') { + throw new Error("JSDO: JSDO event listener for " + prop + " is not a function."); + } + /* scope is optional, but must be an object if provided */ + if (el.scope && (typeof el.scope) !== 'object') { + throw new Error("JSDO: JSDO event listener scope for " + prop + " is not an object."); + } + }); + } + } + + if (this.name) { + // Read resource definition from the Catalog - save reference to JSDO + // Enhance this to deal with multiple services loaded and the same resource + // name is used by more than one service (use the local serviceName var) + this._resource = progress.data.ServicesManager.getResource(this.name); + if (this._resource) { + if (!this.url) + this.url = this._resource.url; + if (!this._dataSetName && this._resource._dataSetName) { + // Catalog defines a DataSet + this._dataSetName = this._resource._dataSetName; + + // Define TableRef property in the JSDO + if (this._resource.dataProperty) { + var buffer = this[this._resource.dataProperty] + = new progress.data.JSTableRef(this, this._resource.dataProperty); + this._buffers[this._resource.dataProperty] = buffer; + } + else { + for (var tableName in this._resource.fields) { + var buffer = this[tableName] + = new progress.data.JSTableRef(this, tableName); + this._buffers[tableName] = buffer; + } + } + } + if (!this._dataProperty && this._resource.dataProperty) + this._dataProperty = this._resource.dataProperty; + + if (!this._dataSetName) { + var tableName = this._dataProperty ? this._dataProperty : ""; + this._buffers[tableName] = new progress.data.JSTableRef(this, tableName); + if (tableName) + this[tableName] = this._buffers[tableName]; + } + + // Add functions for operations to JSDO object + for (var fnName in this._resource.fn) { + this[fnName] = this._resource.fn[fnName]["function"]; + } + // Check if CUD operations have been defined + this._hasCUDOperations = + this._resource.generic["create"] !== undefined + || this._resource.generic["update"] !== undefined + || this._resource.generic["delete"] !== undefined; + this._hasSubmitOperation = this._resource.generic["submit"] !== undefined; + + /* get a session object, using name of the service to look it up in the list of + * sessions maintained by the ServicesManager + */ + if (!this._session) { + var myservice = progress.data.ServicesManager.getService(this._resource.service.name); + this._session = myservice._session; + this._session._pushJSDOs(this); + } + } + else { + throw new Error(msg.getMsgText("jsdoMSG004", this.name)); + } + } + else { + this._buffers[""] = new progress.data.JSTableRef(this, ""); + } + + if (!this._session) { + throw new Error("JSDO: Unable to get user session for resource '" + this.name + "'"); + } + + // Calculate _numBuffers and _defaultTableRef + for (var buf in this._buffers) { + this._buffers[buf]._parent = null; + this._buffers[buf]._children = []; + // The _relationship object is only specified for the child buffer. + // Currently it is limited to only a single relationship. ie. It does not support the + // where the child buffer is involved in more than one data-relation + this._buffers[buf]._relationship = null; + this._buffers[buf]._isNested = false; + if (!this._defaultTableRef) + this._defaultTableRef = this._buffers[buf]; + this._numBuffers++; + } + if (this._numBuffers != 1) + this._defaultTableRef = null; + else { + // record is used to represent the current record for a table reference + // data corresponds to the values (JSON object) of the data + this.record = null; + } + + // Define caseSensitive property at the JSDO level + if ((typeof Object.defineProperty) == 'function') { + this._caseSensitive = false; // caseSensitive is false by default + Object.defineProperty( + this, + "caseSensitive", + { + get: function () { + return this._caseSensitive; + }, + set: function (value) { + this._caseSensitive = value ? true : false; + + for (var buf in this._buffers) { + this._buffers[buf].caseSensitive = this._caseSensitive; + } + }, + enumerable: true, + writeable: true + }); + this._autoSort = true; // autoSort is true by default + Object.defineProperty( + this, + "autoSort", + { + get: function () { + return this._autoSort; + }, + set: function (value) { + this._autoSort = value ? true : false; + + for (var buf in this._buffers) { + this._buffers[buf].autoSort = this._autoSort; + } + }, + enumerable: true, + writeable: true + }); + } + + // Define _properties property at the JSDO level + this._properties = {}; + if ((typeof Object.defineProperty) == 'function') { + Object.defineProperty( this, + "this._properties", + { + get: function () { + return this._properties; + }, + enumerable: false + } + ); + + } + + + // Set schema for TableRef + if (this._resource && this._resource.fields) { + for (var buf in this._buffers) { + this._buffers[buf]._schema = this._resource.fields[buf]; + this._buffers[buf]._primaryKeys = this._resource.primaryKeys[buf]; + + // Create _fields object used to validate fields as case-insensitive. + this._buffers[buf]._fields = {}; + var fields = this._buffers[buf]._schema; + for (var i = 0; i < fields.length; i++) { + this._buffers[buf]._fields[fields[i].name.toLowerCase()] = fields[i]; + if (typeof(fields[i].origName) !== "undefined") { + if ((typeof(fields[i].origName) !== "string") + || (fields[i].origName.trim() === "")) { + throw new Error(msg.getMsgText("jsdoMSG504", + "JSDO", "Field '" + fields[i].name + "' in resource '" + this._resource.name + "'", "origName")); + } + } + } + + if (this._buffers[buf]._schema && (typeof Object.defineProperty) == 'function') { + // Add fields as properties of the TableRef object + for (var i = 0; i < this._buffers[buf]._schema.length; i++) { + var fieldName = this._buffers[buf]._schema[i].name, + fieldInfo = this._buffers[buf]._schema[i]; + if (typeof(this._buffers[buf][fieldName]) == 'undefined') { + this._defineProperty(buf, fieldName); + } + if (fieldInfo.type === "array") { + for (var j = 0; j < fieldInfo.maxItems; j += 1) { + var name = fieldName + progress.data.JSDO.ARRAY_INDEX_SEPARATOR + (j + 1); + // Skip element if a field with the same name exists + // Only create property if the name is not being used + if (!this._buffers[buf]._fields[name.toLowerCase()]) { + this._defineProperty(buf, name); + } + } + } + } + } + } + // Set schema for when dataProperty is used but not specified via the catalog + if (this._defaultTableRef + && !this._defaultTableRef._schema + && this._resource.fields[""]) { + this._defaultTableRef._schema = this._resource.fields[""]; + } + } + else { + if (this._defaultTableRef) + this._defaultTableRef._schema = []; + } + + // Set isNested property + if (this._numBuffers > 1) { + for (var buf in this._buffers) { + var fields = []; + var found = false; + for (var i = 0; i < this._buffers[buf]._schema.length; i++) { + var field = this._buffers[buf]._schema[i]; + + if (field.items + && field.type == "array" && field.items.$ref) { + if (this._buffers[field.name]) { + found = true; + this._buffers[field.name]._isNested = true; + } + } + else + fields.push(field); + } + // Replace list of fields - removing nested datasets from schema + if (found) + this._buffers[buf]._schema = fields; + } + } + + // Process relationships + if (this._resource && this._resource.relations) { + for (var i = 0; i < this._resource.relations.length; i++) { + var relationship = this._resource.relations[i]; + + // Set relationship information ignoring self-referencing (recursive) relationships + if (relationship.childName + && relationship.parentName + && (relationship.childName !== relationship.parentName)) { + // Set casing of fields in relationFields to be the same as in the schema + if (relationship.relationFields instanceof Array) { + for (var j = 0; j < relationship.relationFields.length; j++) { + var fieldName; + var field; + if (this._buffers[relationship.parentName]._fields) { + fieldName = relationship.relationFields[j].parentFieldName; + field=this._buffers[relationship.parentName]._fields[fieldName.toLowerCase()]; + if (field) { + relationship.relationFields[j].parentFieldName = field.name; + } + else + throw new Error(msg.getMsgText("jsdoMSG010", fieldName)); + } + if (this._buffers[relationship.childName]._fields) { + fieldName = relationship.relationFields[j].childFieldName; + field=this._buffers[relationship.childName]._fields[fieldName.toLowerCase()]; + if (field) { + relationship.relationFields[j].childFieldName = field.name; + } + else + throw new Error(msg.getMsgText("jsdoMSG010", fieldName)); + } + } + } + this._buffers[relationship.childName]._parent = relationship.parentName; + this._buffers[relationship.childName]._relationship = relationship.relationFields; + this._buffers[relationship.parentName]._children.push(relationship.childName); + } + } + } + + this._getDefaultValue = function (field) { + var defaultValue, + t, m, d, + isDate = false; + + if ((field.type === "string") + && field.format + && (field.format.indexOf("date") !== -1) + && (field["default"])) { + isDate = true; + } else if ((field.type === "array") + && field.ablType + && (field.ablType.indexOf("DATE") != -1) + && (field["default"])) { + isDate = true; + } else { + defaultValue = field["default"]; + } + + if (isDate) { + switch (field["default"].toUpperCase()) { + case "NOW": + defaultValue = new Date().toISOString(); + break; + case "TODAY": + t = new Date(); + m = String((t.getMonth() + 1)); + if (m.length === 1) { + m = '0' + m; + } + d = String((t.getDate())); + if (d.length === 1) { + d = '0' + d; + } + defaultValue = t.getFullYear() + '-' + m + '-' + d; + break; + default: + defaultValue = field["default"]; + } + } + + return defaultValue; + }; + + // Method to calculate the element information of an array given the name, index, and value + // Parameters: + // arrayFieldName The name o the field + // index Optional parameter - if index is null/undefined the name of the element is the prefix + // value Optional parameter + this._getArrayField = function (arrayFieldName, index, value) { + var element = {}; + // ABL arrays are 1-based + element.name = arrayFieldName + progress.data.JSDO.ARRAY_INDEX_SEPARATOR + ((index >= 0) ? (index + 1) : ""); + element.value = value ? value[index] : undefined; + return element; + }; + + this.isDataSet = function () { + return this._dataSetName ? true : false; + }; + + /* handler for invoke operation complete */ + this._invokeComplete = function (jsdo, success, request) { + // only fire on async requests + if (request.async && request.fnName) { + jsdo.trigger('afterInvoke', request.fnName, jsdo, success, request); + } + + if (request.deferred) { + if (success) { + request.deferred.resolve(jsdo, success, request); + } + else { + request.deferred.reject(jsdo, success, request); + } + } + }; + + /* handler for invoke operation success */ + this._invokeSuccess = function (/* jsdo, success, request */) { + // do nothing + }; + + /* handler for invoke operation error */ + this._invokeError = function (/* jsdo, success, request */) { + // do nothing + }; + + /* + * Performs an HTTP request using the specified parameters. This is + * used to perform remote calls for the JSDO for operations defined. + * + */ + this._httpRequest = function (xhr, method, url, reqBody, request) { + + function afterOpenRequest() { + var input = null; + if (reqBody) { + xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); + input = JSON.stringify(reqBody); + } + + try { + xhr.send(input); + } catch (e) { + request.success = false; + request.exception = e; + // let Session check for online/offline + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + } + } + + // if xhr wasn't passed we'll create our own since this is an invoke operation + // if xhr is passed, then it is probably a CRUD operation which is setup with XHR + // in call to session + if (!xhr) { + xhr = new XMLHttpRequest(); + + // only setup the callback handlers if we're responsible for creating the + // xhr call which happens on invoke operations...which is the normal case + // the CRUD operations setup their own callbacks and they have their own + // event handlers so we don't use them here. + xhr.onCompleteFn = this._invokeComplete; + xhr.onSuccessFn = this._invokeSuccess; + xhr.onErrorFn = this._invokeError; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + + // for invokes we always fire the invoke when doing async + if (request.async && request.fnName) { + this.trigger('beforeInvoke', request.fnName, this, request); + } + + // For Invoke operations, wrap reqBody in a request object + // This is not required for CRUD operations since the whole + // reqBody is mapped to the parameter + if (reqBody) { + if (this._resource && this._resource.service) { + var useRequest = this._resource.service.useRequest; + if (this._resource.service.settings + && this._resource.service.settings.useRequest !== undefined) { + useRequest = this._resource.service.settings.useRequest; + } + if (useRequest) { + reqBody = {request: reqBody}; + } + } + } + } + + xhr.request = request; + xhr.jsdo = this; + request.jsdo = this; + request.xhr = xhr; + + this._session._openRequest(xhr, method, url, request.async, afterOpenRequest); + + return request; // Note: for the async case, this does not give us exactly the same behavior + // as when afterOpenRequest is called synchronously, because this returns + // request before its xhr has had its open() called + }; + + + // This method currently is just used by the JSDOReadService. + // It returns data in its non-nested (default) format + this._getDataObject = function () { + var dataObject = {}; + if (this._dataSetName) { + dataObject[this._dataSetName] = {}; + + var oldUseRelationships = this.useRelationships; + // Turn off useRelationships so that getData() returns all the records + try { + this.useRelationships = false; + for (var buf in this._buffers) { + dataObject[this._dataSetName][buf] = this._buffers[buf].getData(); + } + } + finally { + // Restore useRelationships + this.useRelationships = oldUseRelationships; + } + } + else { + if (this._dataProperty) { + dataObject[this._dataProperty] = this.getData(); + } + else + return this.getData(); // Array + } + return dataObject; + }; + + + // This method currently is just used by the JSDOReadService. + // Now that the JSDO Services support nested data, we want to return data nested for those + // relationships that are marked nested. + // + // This method returns a data object containing the nested data. + // If a parent row is involved in nested relationship, + // then references to its child rows are added to the parent row in a child table array + // (providing the nested format). + // We are using the internal jsdo _data arrays, + // and adding a child table array to each parent row that has children. + // Once the caller is done with the nested data, + // they can call jsdo._unnestData() which removes these child table references + // + this._getDataObjectAsNested = function () { + var dataObject = {}; + if (this._dataSetName) { + dataObject[this._dataSetName] = {}; + + try { + // First walk thru all buffers. We need to determine if any of the buffers are + // involved in a nested relationship. If so, we want to return the child's + // data in nested format. + for (var buf in this._buffers) { + var bufObj = this._buffers[buf]; + + + // If this is a child table, and its involved in a nested relationship, + // then just skip. + // This table's data will be nested within each parent row when we + // process the parent table. + if (bufObj._isNested) continue; + + this._nestChildren = false; // default to false + + // If table has children, see if any relationship is NESTED + if (bufObj._children.length > 0) { + for (var i = 0; i < bufObj._children.length; i++) { + var childBufObj = this._buffers[bufObj._children[i]]; + + if (childBufObj._isNested) { + this._nestChildren = true; + break; + } + } + } + + dataObject[this._dataSetName][buf] = this._buffers[buf].getData(); + } + } + catch (e) { + throw new Error(msg.getMsgText("jsdoMSG000", e.message)); + } + finally { + // Set back to default avlue + this._nestChildren = false; + } + } + else { + if (this._dataProperty) { + dataObject[this._dataProperty] = this.getData(); + } + else + return this.getData(); // Array + } + return dataObject; + }; + + + // This method is used in conjunction with _getDataObjectAsNested() in the JSDOReadService. + // _getDataObjectAsNested() adds arrays of child row references to their parent rows. + // Once the JSDOReadService has done its data mapping, we need to remove the references since + // internally the JSDO stores its data in unnested format. + this._unnestData = function () { + + if (this._dataSetName) { + var parentRecord; + var bufObj; + var childBufObj; + + // First walk thru all buffers. We need to determine if any of the buffers are parent + // buffers involved in a nested relationship. If so, then we'll look for any child row arrays + // to delete + for (var buf in this._buffers) { + bufObj = this._buffers[buf]; + + // If we know this table has at least one nested child table, we'll walk thru + // all its rows to determine if the rows have any child row arrays. + // It's more efficient to just walk thru the parent row list once, so we'll + // check for all child row arrays here + + if (bufObj._hasNestedChild()) { + // Now must walk thru the parent rows and delete any child row arrays + for (var i = 0; i < bufObj._data.length; i++) { + parentRecord = bufObj._data[i]; + + for (var j = 0; j < bufObj._children.length; j++) { + childBufObj = this._buffers[bufObj._children[j]]; + + if (parentRecord[childBufObj._name]) { + delete parentRecord[childBufObj._name]; + } + } + + } + } + } // end for + } + }; + + + this._recToDataObject = function (record, includeChildren) { + if (this._defaultTableRef) + return this._defaultTableRef._recToDataObject(record, includeChildren); + throw new Error(msg.getMsgText("jsdoMSG001", "_recToDataObject()")); + }; + + this._recFromDataObject = function (dataObject) { + if (this._defaultTableRef) + return this._defaultTableRef._recFromDataObject(dataObject); + throw new Error(msg.getMsgText("jsdoMSG001", "_recFromDataObject()")); + }; + + this.add = function (obj) { + if (this._defaultTableRef) + return this._defaultTableRef.add(obj); + throw new Error(msg.getMsgText("jsdoMSG001", "add() or create()")); + }; + + // Alias for add() method + this.create = this.add; + + this.hasData = function () { + for (var buf in this._buffers) { + if (this._buffers[this._buffers[buf]._name].hasData()) + return true; + } + return false; + }; + + this.getData = function (params) { + if (this._defaultTableRef) + return this._defaultTableRef.getData(params); + throw new Error(msg.getMsgText("jsdoMSG001", "getData()")); + }; + + this.getSchema = function () { + if (this._defaultTableRef) + return this._defaultTableRef.getSchema(); + throw new Error(msg.getMsgText("jsdoMSG001", "getSchema()")); + }; + + this.findById = function (id) { + if (this._defaultTableRef) + return this._defaultTableRef.findById(id); + throw new Error(msg.getMsgText("jsdoMSG001", "findById()")); + }; + + this._convertType = function (value, type, itemType) { + if ((typeof value != 'string') || (type === null)) return value; + var result = value; + try { + if (type == 'array') { + var result = []; + + value = value.slice(1, value.length - 1); + var elements = value.split(','); + var convertItem = (itemType && (itemType != 'string')); + for (var i = 0; i < elements.length; i++) { + result[i] = convertItem ? this._convertType(elements[i], itemType, null) : elements[i]; + } + } + else if (type == 'integer') { + result = parseInt(value); + } + else if (type == 'number') { + result = parseFloat(value); + } + else { + result = value; + } + } + catch (e) { + throw new Error(msg.getMsgText("jsdoMSG000", + "Error converting string to native type: " + e.message)); + } + return result; + }; + + this.assign = function (values) { + if (this._defaultTableRef) { + return this._defaultTableRef.assign(values); + } + else + throw new Error(msg.getMsgText("jsdoMSG001", "assign() or update()")); + }; + + // Alias for assign() method + this.update = this.assign; + + this.remove = function () { + if (this._defaultTableRef) { + return this._defaultTableRef.remove(); + } + else + throw new Error(msg.getMsgText("jsdoMSG001", "remove()")); + }; + + this.getId = function () { + if (this._defaultTableRef) + return this._defaultTableRef.getId(); + throw new Error(msg.getMsgText("jsdoMSG001", "getId()")); + }; + + // getErrors() - JSDO + this.getErrors = function () { + if (this._defaultTableRef) + return this._defaultTableRef.getErrors(); + throw new Error(msg.getMsgText("jsdoMSG001", "getErrors()")); + }; + + this.getErrorString = function () { + if (this._defaultTableRef) + return this._defaultTableRef.getErrorString(); + throw new Error(msg.getMsgText("jsdoMSG001", "getErrorString()")); + }; + + /* + * Finds a record in the JSDO memory using the specified function to determine the record. + */ + this.find = function (fn) { + if (this._defaultTableRef) + return this._defaultTableRef.find(fn); + throw new Error(msg.getMsgText("jsdoMSG001", "find()")); + }; + + this.foreach = function (fn) { + if (this._defaultTableRef) + return this._defaultTableRef.foreach(fn); + throw new Error(msg.getMsgText("jsdoMSG001", "foreach()")); + }; + + this.setSortFields = function (sortFields) { + if (this._defaultTableRef) + return this._defaultTableRef.setSortFields(sortFields); + throw new Error(msg.getMsgText("jsdoMSG001", "setSortFields()")); + }; + + this.setSortFn = function (fn) { + if (this._defaultTableRef) + return this._defaultTableRef.setSortFn(fn); + throw new Error(msg.getMsgText("jsdoMSG001", "setSortFn()")); + }; + + this.sort = function (arg1) { + if (this._defaultTableRef) + return this._defaultTableRef.sort(arg1); + throw new Error(msg.getMsgText("jsdoMSG001", "sort()")); + }; + + this._clearErrors = function () { + this._lastErrors = []; + for (var buf in this._buffers) { + this._buffers[buf]._lastErrors = []; + } + }; + + /** + * setAllRecordsRejected + * + * Sets _allRecordsRejected flag to indicate whether all records have been rejected + * in a saveChanges() call. + * If changes are specified as an array, the changes are used to calculate the flag. + * + * @param {*} param - Array with changes or boolean with value + */ + this._setAllRecordsRejected = function (param) { + var changes, + hasErrors, + hasRejected, + hasCommittedRecords, + i; + + // Note: This function is a single one-stop convenient function to set + // _allRecordsRejected and _someRecordsRejected. + // This logic can be optimized by setting the flags while processing the response. + if (param instanceof Object) { + if (param instanceof Array) { + changes = param; + hasErrors = false; + + this._allRecordsRejected = false; + this._someRecordsRejected = false; + + for (var buf in this._buffers) { + if (this._buffers[buf]._lastErrors.length > 0) { + hasErrors = true; + } + } + if (hasErrors) { + this._allRecordsRejected = true; + this._someRecordsRejected = true; + + for (i = 0; i < changes.length; i += 1) { + if (changes[i].record && !changes[i].record.data._rejected) { + this._allRecordsRejected = false; + return; + } + } + } else if (changes.length > 0) { + this._allRecordsRejected = true; + this._someRecordsRejected = false; + hasCommittedRecords = false; + + for (i = 0; i < changes.length; i += 1) { + if (changes[i].record) { + if (changes[i].record.data._rejected) { + this._someRecordsRejected = true; + } else { + hasCommittedRecords = true; + } + } + } + if (hasCommittedRecords && !this._someRecordsRejected) { + this._allRecordsRejected = false; + } + } + } else { + if (param.operations instanceof Array) { + if (param.operations.length > 0 + && !param.operations[0].success) { + // First operation failed + this._allRecordsRejected = true; + this._someRecordsRejected = true; + + for (i = 0; i < param.operations.length; i += 1) { + if (param.operations[i].success) { + this._allRecordsRejected = false; + return; + } + } + } else { + // Not all operations were rejected + this._allRecordsRejected = false; + this._someRecordsRejected = false; + + for (i = 0; i < param.operations.length; i += 1) { + if (!param.operations[i].success) { + this._someRecordsRejected = true; + return; + } + } + } + } + } + } else { + // Possible values: true, false, undefined + this._allRecordsRejected = param; + this._someRecordsRejected = param; + } + }; + + /* + * Loads data from the HTTP resource. + */ + this.fill = function () { + var objParam, + promise, + properties, + mapping; + + // Clear errors before sending request + this._clearErrors(); + + // Reset _allRecordsRejected + this._setAllRecordsRejected(undefined); + + // Process parameters + if (arguments.length !== 0) { + // Call to fill() has parameters + if (typeof(arguments[0]) == 'function') { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "fill() or read()")); + } + + properties = this.getMethodProperties("read"); + + // Get plugin if mappingType is not undefined, null, or "" + if (properties && properties.mappingType) { + mapping = progress.data.PluginManager.getPlugin(properties.mappingType); + if (!mapping) { + throw new Error(msg.getMsgText("jsdoMSG118", properties.mappingType)); + } + } + + // fill( string); + var filter; + if (arguments[0] === null || arguments[0] === undefined) { + filter = ""; + } + else if (typeof(arguments[0]) == "string") { + filter = arguments[0]; + objParam = {filter: filter}; + } + else if (typeof(arguments[0]) == "object") { + // options + // ablFilter, id, top, skip, sort + + // Use plugin if mappingType is not undefined, null, or "" + if (mapping) { + if (typeof(mapping.requestMapping) === "function") { + objParam = mapping.requestMapping(this, arguments[0], { operation: "read" }); + } + else { + objParam = arguments[0]; + } + } + else { + if (properties.capabilities) { + throw new Error(msg.getMsgText("jsdoMSG119")); + } + objParam = arguments[0]; + } + } + else { + throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "fill() or read()")); + } + } + else { + // fill(); + objParam = null; + } + + var xhr = new XMLHttpRequest(); + var request = { + xhr: xhr, + jsdo: this, + objParam: objParam + }; + + xhr.request = request; + xhr.jsdo = this; + + xhr.onSuccessFn = this._fillSuccess; + xhr.onErrorFn = this._fillError; + xhr.onCompleteFn = this._fillComplete; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + + this.trigger("beforeFill", this, request); + + if (this._resource) { + if (typeof(this._resource.generic.read) == "function") { + xhr.objParam = objParam; + this._resource.generic.read.call(this, xhr, this._async); + if (xhr.request.deferred) { + promise = xhr.request.deferred.promise(); + } + } + else { + throw new Error("JSDO: READ operation is not defined."); + } + } + else { + // Old approach to call READ + this._session._openRequest(xhr, 'GET', this.url, this._async); + try { + xhr.send(null); + } + catch (e) { + request.exception = e; + // get the Client Context ID (AppServer ID) + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + } + } + + // This is the scenario where the read.call did not reach server. i.e., + // some problem in between making successful call to server and we are + // completing the fill() operation with necessary cleanup operations + if (request.success == false && request.exception) { + if ((typeof xhr.onErrorFn) == 'function') { + xhr.onErrorFn(xhr.jsdo, request.success, request); + } + + // get the Client Context ID (AppServer ID) + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + + if ((typeof xhr.onCompleteFn) == 'function') { + xhr.onCompleteFn(xhr.jsdo, request.success, request); + } + } + + return promise; + }; + + // Alias for fill() method + this.read = this.fill; + + /* + * Clears all data (including any pending changes) for each buffer in JSDO + */ + this._clearData = function () { + for (var buf in this._buffers) { + this._buffers[buf]._clearData(); + } + }; + + /* + * Executes a CRUD operation using the built-in API. + */ + this._execGenericOperation = function (operation, objParam, request, + onCompleteFn, onSuccessFn, onErrorFn) { + + var xhr = new XMLHttpRequest(); + request.xhr = xhr; + request.jsdo = this; + request.objParam = objParam; + request.operation = operation; + xhr.jsdo = this; + xhr.onCompleteFn = onCompleteFn; + xhr.onSuccessFn = onSuccessFn; + xhr.onErrorFn = onErrorFn; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + xhr.request = request; + + this._convertRequestData(objParam); + + var operationStr; + switch (operation) { + case progress.data.JSDO._OP_READ: + case progress.data.JSDO._OP_CREATE: + case progress.data.JSDO._OP_UPDATE: + case progress.data.JSDO._OP_DELETE: + case progress.data.JSDO._OP_SUBMIT: + operationStr = PROGRESS_JSDO_OP_STRING[operation]; + break; + default: + throw new Error("JSDO: Unexpected operation " + operation + " in HTTP request."); + } + + if (this._resource) { + if (typeof(this._resource.generic[operationStr]) == "function") { + xhr.objParam = objParam; + this._resource.generic[operationStr](xhr, this._async); + } + else { + // "JSDO: {1} operation is not defined." + throw new Error(msg.getMsgText("jsdoMSG046", operationStr.toUpperCase() )); + } + } + }; + + // Determines if any fields need a conversion when data sent to backend + this._initConvertForServer = function () { + var i, buf, schema; + + // If set, we're good. Field lists for conversion have already been created + if (this._convertForServer !== undefined) { + return; + } + + this._convertForServer = false; + for (buf in this._buffers) { + schema = this._buffers[buf].getSchema(); + this._buffers[buf]._convertFieldsForServer = []; + this._buffers[buf]._convertForServer = false; + + // Check if any fields need conversion + for (i = 0; i < schema.length; i++) { + if (schema[i].ablType && this._ablTypeNeedsConversion(schema[i].ablType)) { + this._buffers[buf]._convertFieldsForServer.push({name: schema[i].name, + ablType: schema[i].ablType}); + } + } + if (this._buffers[buf]._convertFieldsForServer.length > 0) { + this._convertForServer = true; + this._buffers[buf]._convertForServer = true; + } + } + }; + + this._convertRequestData = function (objParam) { + var buf, + beforeData; + + if (this._convertForServer === false) { + return; + } + + // We know at least one table has a field to convert + for (buf in this._buffers) { + if (this._buffers[buf]._convertForServer) { + if (objParam[this._dataSetName]) { + // First convert after-table + if (objParam[this._dataSetName][buf]) { + this._convertTableData(this._buffers[buf], objParam[this._dataSetName][buf]); + } + + // Now let's convert before-image data + beforeData = objParam[this._dataSetName]["prods:before"]; + if (beforeData && beforeData[buf]) { + this._convertTableData(this._buffers[buf], beforeData[buf]); + } + } + // This is for case where saveChanges(false) is called with no before-image data + else if (objParam[buf]) { + this._convertTableData(this._buffers[buf], objParam[buf]); + } + } + } + }; + + this._convertTableData = function (tableRef, tableData) { + var i; + + for (i = 0; i < tableData.length; i++) { + this._convertRowData(tableRef, tableData[i]); + } + }; + + this._convertRowData = function (tableRef, record) { + var i, + field; + + for (i = 0; i < tableRef._convertFieldsForServer.length; i += 1) { + field = tableRef._convertFieldsForServer[i]; + record[field.name] = this._convertField(record[field.name], field.ablType); + } + }; + + this._convertField = function (value, ablType) { + var result; + + if (value === undefined || value === null) { + return value; + } + + if (value instanceof Array) { + var resultArray = []; + for (var i = 0; i < value.length; i++) { + resultArray[i] = this._convertField(value[i], ablType); + } + return resultArray; + } + + try { + switch (ablType.toUpperCase()) { + case "DATE": + case "DATETIME": + if (typeof value === 'string') { + result = value; + } + else if (value instanceof Date) { + result = this._convertDate(value, ablType.toUpperCase()); + } + else { + throw new Error("Unexpected value for " + ablType.toUpperCase() + "."); + } + break; + default: + result = value; + break; + } + } + catch (e) { + throw new Error(msg.getMsgText("jsdoMSG000", + "Error in _convertField for value: " + value + ". " + e.message)); + } + + return result; + }; + + // Convert Date object to string for DATE and DATETIME ablTypes + // Not necessary to do for DATETIME-TZ since JSON.stringify() will do correct conversion + this._convertDate = function (value, ablType) { + var result = value; + + // DATE format should be in ISO 8601 format yyyy-mm-dd + // DATETIME format should be in ISO 8601 format yyyy-mm-ddThh:mm:ss.sss + if (ablType === "DATE" || ablType === "DATETIME") { + result = progress.util._pad(value.getFullYear(), 4) + '-' + + progress.util._pad(value.getMonth() + 1) + '-' + + progress.util._pad(value.getDate()); + + if (ablType === "DATETIME") { + result = result + "T" + + progress.util._pad(value.getHours()) + ":" + + progress.util._pad(value.getMinutes()) + ":" + + progress.util._pad(value.getSeconds()) + "." + + progress.util._pad(value.getMilliseconds(), 3); + } + } + + return result; + }; + + + this._ablTypeNeedsConversion = function (ablType) { + + var needsConversion = false; + + switch (ablType.toUpperCase()) { + case "DATE": + case "DATETIME": + needsConversion = true; + break; + } + + return needsConversion; + }; + + + + this._undefWorkingRecord = function () { + // Set record property + for (var buf in this._buffers) { + this._buffers[buf]._setRecord(null); + } + }; + + /* + * Saves changes in the JSDO. Save any outstanding changes for CREATES, UPDATE, and DELETEs + */ + this.saveChanges = function (useSubmit) { + var promise, + request; + + if (useSubmit === undefined) { + useSubmit = false; + } + else if (typeof(useSubmit) != 'boolean') { + throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "saveChanges()")); + } + + // _fireCUDTriggersForSubmit() needs to know how saveChanges() was called + this._useSubmit = useSubmit; + + // confirm the availability of the operations required for executing this saveChanges call + // (_checkThatJSDOHasRequiredOperations() throws an error if there's a missing operation, + // which this method deliberately allows to bubble up to the caller) + this._checkThatJSDOHasRequiredOperations(); + + // Don't allow Submit with just a temp-table if autoApplyChanges is true + if ( !this._dataSetName && this._useSubmit && this.autoApplyChanges) { + /* error message: "autoApplyChanges is not supported for submit with a temp-table */ + /* Use jsdo.autoApplyChanges = false." */ + throw new Error(msg.getMsgText("jsdoMSG124")); + } + + // Check if any data being sent to server needs to first be converted + this._initConvertForServer(); + + // Clear errors before sending request + this._clearErrors(); + + // Reset _allRecordsRejected + this._setAllRecordsRejected(undefined); + + request = { + jsdo: this + }; + + this.trigger("beforeSaveChanges", this, request); + + if (useSubmit) { + /* Pass in request object. + * Need to use same request object so before and after saveChanges events + * are in sync in JSDO Submit Service. */ + promise = this._syncDataSetForSubmit(request); + } + else if (this._dataSetName) { + promise = this._syncDataSetForCUD(); + } + else { + promise = this._syncSingleTable(); + } + + return promise; + }; + + /* + * _checkThatJSDOHasRequiredOperations + + This method is intended to be used by the saveChanges() method to determine whether + the JSDO's resource definition includes the operations necessary for executing the + types of changes that are pending in the JSDO. It checks for Submit if saveChanges + was called with useSubmit set to true, otherwise it checks whatever CUD operations are + pending. + The JSDO's internal _useSubmit property must be set correctly before this method + is called + */ + this._checkThatJSDOHasRequiredOperations = function( ) { + var checkedDelete = false, + checkedCreate = false, + checkedUpdate = false, + buf, + tableRef; + + if (!this._hasCUDOperations && !this._hasSubmitOperation) { + throw new Error(msg.getMsgText("jsdoMSG026")); + } + + // Validate the use of Submit + if (this._useSubmit) { + if (!this._hasSubmitOperation) { + // "JSDO: {1} operation is not defined."; + throw new Error(msg.getMsgText("jsdoMSG046", "SUBMIT")); + } + else { + return; + } + } + + if (!this._resource) { + // Need the _resource property to do the validation. If not present, just return + // and let execution run as normal (presumably there will be an error) + return; + } + + // Find the pending operations and make sure they are defined + for (buf in this._buffers) { + + tableRef = this._buffers[buf]; + + if (!checkedDelete && tableRef._deleted.length > 0) { + this._confirmOperationExists( progress.data.JSDO._OP_DELETE ); + checkedDelete = true; + } + + if (!checkedCreate && tableRef._added.length > 0) { + this._confirmOperationExists( progress.data.JSDO._OP_CREATE ); + checkedCreate = true; + } + + if (!checkedUpdate && Object.keys(tableRef._changed).length > 0) { + this._confirmOperationExists( progress.data.JSDO._OP_UPDATE ); + checkedUpdate = true; + } + + if ( checkedDelete && checkedCreate && checkedUpdate ) { + break; + } + } + + }; + + // Determines whether a given operation is defined by the JSDO's resource + // throws an error if it's not defined + this._confirmOperationExists = function(operation) { + var operationStr = PROGRESS_JSDO_OP_STRING[operation]; + if (typeof(this._resource.generic[operationStr]) !== "function") { + // "JSDO: {1} operation is not defined." + throw new Error(msg.getMsgText("jsdoMSG046", operationStr.toUpperCase() )); + } + }; + + this.invoke = function (name, object) { + var request = this[name](object); + if (request.deferred) { + return request.deferred.promise(); + } + + return undefined; + }; + + /* + * Synchronizes changes for a TableRef + * + * @param operation HTTP operation to be performed + * @param tableRef Handle to the TableRef + * @param batch Optional. batch information associated with the sync operation. + * If not specified a new one will be created. Used for saving datasets. + */ + this._syncTableRef = function (operation, tableRef, batch) { + var rowData, + requestData, + jsonObject; + + if (tableRef._visited) return; + tableRef._visited = true; + + //ensure batch object is sane + if (!batch) { + batch = { + operations: [] + }; + } else if (!batch.operations) { + batch.operations = []; + } + + // Before children + // Create parent records before children + switch (operation) { + case progress.data.JSDO._OP_CREATE: + for (var i = 0; i < tableRef._added.length; i++) { + var id = tableRef._added[i]; + var jsrecord = tableRef._findById(id, false); + + if (!jsrecord) continue; + if (tableRef._processed[id]) continue; + tableRef._processed[id] = jsrecord.data; + + rowData = {}; + jsonObject = {}; + + // Make copy of row data, in case we need to convert data for backend.. + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + + if (this.isDataSet()) { + if (this._useBeforeImage("create")) { + jsonObject[this._dataSetName] = {}; + var dataSetObject = jsonObject[this._dataSetName]; + dataSetObject["prods:hasChanges"] = true; + + dataSetObject[tableRef._name] = []; + + // Dont need to send prods:id for create, + // no before table or error table to match + // Dont need to send prods:clientId - since only sending one record + rowData["prods:rowState"] = "created"; + rowData["prods:clientId"] = jsrecord.data._id; + + delete rowData["_id"]; + + dataSetObject[tableRef._name].push(rowData); + } + else { + jsonObject[tableRef._name] = []; + jsonObject[tableRef._name].push(rowData); + } + } + else { + jsonObject = rowData; + } + + + var request = { + operation: operation, + batch: batch, + jsrecord: jsrecord, + jsdo: this + }; + batch.operations.push(request); + + jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); + this.trigger("beforeCreate", this, jsrecord, request); + + this._execGenericOperation( + progress.data.JSDO._OP_CREATE, jsonObject, request, this._createComplete, + this._createSuccess, this._createError); + } + break; + case progress.data.JSDO._OP_UPDATE: + for (var id in tableRef._changed) { + var jsrecord = tableRef._findById(id, false); + + if (!jsrecord) continue; + if (tableRef._processed[id]) continue; + tableRef._processed[id] = jsrecord.data; + + rowData = {}; + jsonObject = {}; + requestData = {}; + + // Make copy of row data, in case we need to convert data for backend.. + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + + var useBeforeImageFormat = false; + if (this.isDataSet()) { + if (this._useBeforeImage("update")) { + useBeforeImageFormat = true; + jsonObject[this._dataSetName] = {}; + var dataSetObject = jsonObject[this._dataSetName]; + dataSetObject["prods:hasChanges"] = true; + dataSetObject[tableRef._name] = []; + + // Dont need to send prods:clientId - since only sending one record + rowData["prods:id"] = jsrecord.data._id; + rowData["prods:rowState"] = "modified"; + rowData["prods:clientId"] = jsrecord.data._id; + delete rowData["_id"]; + + dataSetObject[tableRef._name].push(rowData); + + // Now create before-table data + dataSetObject["prods:before"] = {}; + var beforeObject = dataSetObject["prods:before"]; + beforeObject[tableRef._name] = []; + + var beforeRowData = {}; + // Dont need to send prods:clientId - since only sending one record + beforeRowData["prods:id"] = jsrecord.data._id; + + tableRef._jsdo._copyRecord(tableRef, + tableRef._beforeImage[jsrecord.data._id], beforeRowData); + delete beforeRowData["_id"]; + + beforeObject[tableRef._name].push(beforeRowData); + } + } + + if (!useBeforeImageFormat) { + if (this._resource.service + && this._resource.service.settings + && this._resource.service.settings.sendOnlyChanges) { + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData, + tableRef._beforeImage[jsrecord.data._id]); + + if (this._resource.idProperty) { + requestData[this._resource.idProperty] = + jsrecord.data[this._resource.idProperty]; + } + else { + throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, + " for sendOnlyChanges property")); + } + } + else + requestData = rowData; + + if (this.isDataSet()) { + jsonObject[tableRef._name] = []; + jsonObject[tableRef._name].push(requestData); + } + else { + jsonObject = rowData; + } + } + + var request = { + jsrecord: jsrecord, + operation: operation, + batch: batch, + jsdo: this + }; + batch.operations.push(request); + + jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); + this.trigger("beforeUpdate", this, jsrecord, request); + + this._execGenericOperation( + progress.data.JSDO._OP_UPDATE, jsonObject, request, this._updateComplete, + this._updateSuccess, this._updateError); + } + break; + } + + // Call _syncTableRef on child tables + for (var i = 0; i < tableRef._children.length; i++) { + var childTableName = tableRef._children[i]; + this._syncTableRef( + operation, this._buffers[childTableName], batch); + } + + // After children + // Delete parent records after children + + if (operation == progress.data.JSDO._OP_DELETE) { + for (var i = 0; i < tableRef._deleted.length; i++) { + var id = tableRef._deleted[i]._id; + var jsrecord = tableRef._deleted[i]; + + if (!jsrecord) continue; + tableRef._processed[id] = jsrecord.data; + + rowData = {}; + jsonObject = {}; + requestData = {}; + + // Make copy of row data, in case we need to convert data for backend.. + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + + var useBeforeImageFormat = false; + if (this.isDataSet()) { + if (this._useBeforeImage("delete")) { + useBeforeImageFormat = true; + jsonObject[this._dataSetName] = {}; + var dataSetObject = jsonObject[this._dataSetName]; + dataSetObject["prods:hasChanges"] = true; + + // There is no after tables for deletes, so just create before-table data + dataSetObject["prods:before"] = {}; + var beforeObject = dataSetObject["prods:before"]; + beforeObject[tableRef._name] = []; + + var beforeRowData = {}; + + // Dont need to send prods:id for delete, no after table or error table to match + // Dont need to send prods:clientId - since only sending one record + beforeRowData["prods:rowState"] = "deleted"; + beforeRowData["prods:clientId"] = jsrecord.data._id; + + tableRef._jsdo._copyRecord(tableRef, + tableRef._beforeImage[rowData._id], beforeRowData); + beforeObject[tableRef._name].push(beforeRowData); + } + } + + if (!useBeforeImageFormat) { + if (this._resource.service + && this._resource.service.settings + && this._resource.service.settings.sendOnlyChanges) { + if (this._resource.idProperty) { + requestData[this._resource.idProperty] = + jsrecord.data[this._resource.idProperty]; + } + else { + throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, + " for sendOnlyChanges property")); + } + } + else { + requestData = rowData; + } + + if (this.isDataSet()) { + jsonObject[tableRef._name] = []; + jsonObject[tableRef._name].push(requestData); + } + else { + jsonObject = rowData; + } + } + + var request = { + batch: batch, + jsrecord: jsrecord, + operation: operation, + jsdo: this + }; + + batch.operations.push(request); + + jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); + this.trigger("beforeDelete", this, jsrecord, request); + + this._execGenericOperation( + progress.data.JSDO._OP_DELETE, jsonObject, request, this._deleteComplete, + this._deleteSuccess, this._deleteError); + } + } + }; + + /* + * Returns true if the specified operation type was specified in the catalog as useBeforeImage, + * else it returns false. + */ + this._useBeforeImage = function (opType) { + + for (var idx = 0; idx < this._resource.operations.length; idx++) { + if (this._resource.operations[idx].type == opType) { + return this._resource.operations[idx].useBeforeImage; + } + } + + return false; + }; + + + /* + * Synchronizes changes for a DataSet. This is called when we send over one row at at time + * to Create, Update and Delete methods. + * It handles row with or without before-image data. + */ + this._syncDataSetForCUD = function () { + var batch = { + operations: [] + }, + deferred, + promise; + + if (typeof($) == 'function' && typeof($.Deferred) == 'function') { + deferred = $.Deferred(); + promise = deferred.promise(); + batch.deferred = deferred; + } + + // Process buffers + // Synchronize deletes + for (var buf in this._buffers) { + this._buffers[buf]._visited = false; + } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._syncTableRef( + progress.data.JSDO._OP_DELETE, tableRef, batch); + } + + // Synchronize adds + for (var buf in this._buffers) { + this._buffers[buf]._visited = false; + } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._syncTableRef( + progress.data.JSDO._OP_CREATE, tableRef, batch); + } + + // Synchronize updates + for (var buf in this._buffers) { + this._buffers[buf]._visited = false; + } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._syncTableRef( + progress.data.JSDO._OP_UPDATE, tableRef, batch); + } + + if (this.autoApplyChanges) { + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + tableRef._processed = {}; + tableRef._added = []; + tableRef._changed = {}; + tableRef._deleted = []; + } + } + + // OE00229270 If _async is false, this ensures that afterSaveChanges() is called just once + // We now do this after all operations have been processed + if (!this._async) { + if (this._isBatchComplete(batch)) { + var success = this._isBatchSuccess(batch); + var request = { + batch: batch, + success: success + }; + this._undefWorkingRecord(); + + // Save error messages + this._lastErrors = []; + if (!success && batch.operations) { + this._updateLastErrors(this, batch, null); + } + this._setAllRecordsRejected(batch); + + this._fireAfterSaveChanges(success, request); + } + } + // end OE00229270 + + return promise; + }; + + + /* + * Synchronizes changes for a single table + */ + this._syncSingleTable = function () { + var deferred, promise; + if (!this._defaultTableRef) return; + var tableRef = this._defaultTableRef; + + var batch = { + operations: [] + }; + + if (typeof($) == 'function' && typeof($.Deferred) == 'function') { + deferred = $.Deferred(); + promise = deferred.promise(); + batch.deferred = deferred; + } + + var fireAfterSaveChanges = false; + + // Skip delete for records that were added + // mark them as processed + var addedRecords = {}; + for (var i = 0; i < tableRef._added.length; i++) { + var id = tableRef._added[i]; + addedRecords[id] = id; + } + for (var i = 0; i < tableRef._deleted.length; i++) { + var jsrecord = tableRef._deleted[i]; + if (!jsrecord) continue; + + var id = jsrecord.data._id; + if (addedRecords[id]) { + // Set request object + // Properties async, fnName, objParam, and response + // are not set when the HTTP request is suppressed + var request = { + success: true, + xhr: undefined, + operation: progress.data.JSDO._OP_DELETE, + batch: batch, + jsrecord: jsrecord, + jsdo: this + }; + batch.operations.push(request); + tableRef._processed[id] = jsrecord.data; + + var jsdo = request.jsdo; + try { + request.jsrecord._tableRef.trigger("afterDelete", jsdo, request.jsrecord, + request.success, request); + jsdo.trigger("afterDelete", jsdo, request.jsrecord, request.success, request); + } finally { + request.complete = true; + } + + fireAfterSaveChanges = true; + } + } + addedRecords = null; + + // Synchronize deletes + for (var i = 0; i < tableRef._deleted.length; i++) { + var jsrecord = tableRef._deleted[i]; + if (!jsrecord) continue; + + var id = jsrecord.data._id; + if (tableRef._processed[id]) continue; + + tableRef._processed[id] = jsrecord.data; + fireAfterSaveChanges = false; + + var xhr = new XMLHttpRequest(); + xhr.jsdo = this; + + var request = { + xhr: xhr, + operation: progress.data.JSDO._OP_DELETE, + batch: batch, + jsrecord: jsrecord, + jsdo: this + }; + batch.operations.push(request); + xhr.onCompleteFn = this._deleteComplete; + xhr.onSuccessFn = this._deleteSuccess; + xhr.onErrorFn = this._deleteError; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + xhr.request = request; + + jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); + this.trigger("beforeDelete", this, jsrecord, request); + + var requestData = {}; + if (this._resource.service + && this._resource.service.settings + && this._resource.service.settings.sendOnlyChanges) { + if (this._resource.idProperty) { + requestData[this._resource.idProperty] = jsrecord.data[this._resource.idProperty]; + } + else { + throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, + " for sendOnlyChanges property")); + } + } + else { + // We must copy record in case _convertRowData() needs to make conversion + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData); + } + + if (tableRef._convertForServer) { + this._convertRowData(tableRef, requestData); + } + + if (this._resource) { + if (typeof(this._resource.generic["delete"]) == "function") { + xhr.objParam = requestData; + this._resource.generic["delete"].call(this, xhr, this._async); + } + else { + throw new Error("JSDO: DELETE operation is not defined."); + } + } + else { + this._session._openRequest(xhr, 'DELETE', this.url + '/' + id, true); + try { + xhr.send(null); + } catch (e) { + request.success = false; + request.exception = e; + // let Session check for online/offline + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + } + + } + } + + // Synchronize adds + for (var i = 0; i < tableRef._added.length; i++) { + var id = tableRef._added[i]; + var jsrecord = tableRef._findById(id, false); + var requestData = {}; + + if (!jsrecord) continue; + if (tableRef._processed[id]) continue; + tableRef._processed[id] = jsrecord.data; + fireAfterSaveChanges = false; + + var xhr = new XMLHttpRequest(); + xhr.jsdo = this; + var request = { + xhr: xhr, + jsrecord: jsrecord, + batch: batch, + operation: progress.data.JSDO._OP_CREATE, + jsdo: this + }; + batch.operations.push(request); + xhr.onCompleteFn = this._createComplete; + xhr.onSuccessFn = this._createSuccess; + xhr.onErrorFn = this._createError; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + xhr.request = request; + + jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); + this.trigger("beforeCreate", this, jsrecord, request); + + if (this._resource) { + if (typeof(this._resource.generic.create) == "function") { + this._copyRecord(tableRef, jsrecord.data, requestData); + if (this._resource.idProperty !== undefined && jsrecord.data._id !== undefined) { + // Remove _id when idProperty is set + delete requestData._id; + } + + if (tableRef._convertForServer) { + this._convertRowData(tableRef, requestData); + } + + xhr.objParam = requestData; + + this._resource.generic.create.call(this, xhr, this._async); + } + else { + throw new Error("JSDO: CREATE operation is not defined."); + } + + } + else { + this._session._openRequest(xhr, 'POST', this.url, true); + xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); + this._copyRecord(tableRef, jsrecord.data, requestData); + + if (tableRef._convertForServer) { + this._convertRowData(tableRef, requestData); + } + var input = JSON.stringify(requestData); + + try { + xhr.send(input); + } catch (e) { + request.success = false; + request.exception = e; + // let Session check for online/offline + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + } + + } + } + + // Synchronize updates + for (var id in tableRef._changed) { + var jsrecord = tableRef._findById(id, false); + + if (!jsrecord) continue; + if (tableRef._processed[id]) continue; + tableRef._processed[id] = jsrecord.data; + fireAfterSaveChanges = false; + + var xhr = new XMLHttpRequest(); + var request = { + xhr: xhr, + jsrecord: jsrecord, + operation: progress.data.JSDO._OP_UPDATE, + batch: batch, + jsdo: this + }; + xhr.request = request; + xhr.jsdo = this; + batch.operations.push(request); + xhr.onCompleteFn = this._updateComplete; + xhr.onSuccessFn = this._updateSuccess; + xhr.onErrorFn = this._updateError; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + + jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); + this.trigger("beforeUpdate", this, jsrecord, request); + + var requestData = {}; + if (this._resource.service + && this._resource.service.settings + && this._resource.service.settings.sendOnlyChanges) { + + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData, + tableRef._beforeImage[jsrecord.data._id]); + + if (this._resource.idProperty) { + requestData[this._resource.idProperty] = jsrecord.data[this._resource.idProperty]; + } + else { + throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, + " for sendOnlyChanges property")); + } + } + else { + // We must copy record in case _convertRowData() needs to make conversion + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData); + } + + if (tableRef._convertForServer) { + this._convertRowData(tableRef, requestData); + } + + if (this._resource) { + if (typeof(this._resource.generic.update) == "function") { + xhr.objParam = requestData; + this._resource.generic.update.call(this, xhr, this._async); + } + else { + throw new Error("JSDO: UPDATE operation is not defined."); + } + } + else { + this._session._openRequest(xhr, 'PUT', this.url + '/' + id, this._async); + xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); + + var input = JSON.stringify(requestData); + + try { + xhr.send(input); + } catch (e) { + request.success = false; + request.exception = e; + // let Session check for online/offline + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + } + } + } + + if (this.autoApplyChanges) { + // Arrays to keep track of changes + tableRef._added = []; + tableRef._changed = {}; + tableRef._deleted = []; + tableRef._processed = {}; + } + + // OE00229270 If _async is false, fire afterSaveChanges() after all operations are processed + if (!this._async) + fireAfterSaveChanges = true; + + if (fireAfterSaveChanges) { + var jsdo = this; + var request = { + batch: batch, + success: true + }; + + // Save error messages + jsdo._lastErrors = []; + if (batch.operations) { + jsdo._updateLastErrors(jsdo, batch, null); + } + + jsdo._undefWorkingRecord(); + jsdo._fireAfterSaveChanges(request.success, request); + } + + return promise; + }; + + /************************************************************************ + * + * Synchronizes changes for a DataSet or a temp-table, sending over the entire change-set + * to saveChanges() on server + * If sync'ing a DataSet, sends over before-image and after-image data. + */ + this._syncDataSetForSubmit = function (request) { + var deferred, + promise, + jsonObject, + completeFn = this._saveChangesComplete, + successFn = this._saveChangesSuccess, + errorFn = this._saveChangesError; + + if (typeof($) == 'function' && typeof($.Deferred) == 'function') { + deferred = $.Deferred(); + promise = deferred.promise(); + request.deferred = deferred; + } + + request.jsrecords = []; + + // First thing to do is to create jsonObject with before and after image data for all + // records in change-set (creates, updates and deletes) + if ( this._dataSetName ) { + jsonObject = this._createChangeSet(this._dataSetName, false, request); + } + else { + // just a temp-table. Need to create it somewhat differently from DS + // (no before and after image data) + jsonObject = this._createTTChangeSet(this._defaultTableRef, request); + successFn = this._saveChangesSuccessTT; // will process success response differently from DS + } + + this._execGenericOperation(progress.data.JSDO._OP_SUBMIT, jsonObject, request, + completeFn, successFn, errorFn); + + return promise; + }; + + /************************************************************************ + * + * Private method that creates a jsonObject with before and after image data for all + * records in change-set (creates, updates and deletes) + * + * Params: dataSetName is required. + * alwaysCreateTable is required. If true, always create table array (even if no data/changes) + * request is optional + */ + this._createChangeSet = function (dataSetName, alwaysCreateTable, request) { + var changeSetJsonObject = {}; + + changeSetJsonObject[dataSetName] = {}; + var dataSetJsonObject = changeSetJsonObject[dataSetName]; + + var hasChanges = dataSetJsonObject["prods:hasChanges"] = this._hasChanges(); + if (hasChanges) { + if ((alwaysCreateTable === true)) { + for (var buf in this._buffers) { + dataSetJsonObject[this._buffers[buf]._name] = []; + } + } + + // First do deletes + //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._addDeletesToChangeSet(tableRef, dataSetJsonObject, request); + } + + // Adds + //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._addCreatesToChangeSet(tableRef, dataSetJsonObject, request); + } + + // Updates + //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._addChangesToChangeSet(tableRef, dataSetJsonObject, request); + } + + // Clear _processed map + for (var buf in this._buffers) { + this._buffers[buf]._processed = {}; + } + } + + // Check if change set is empty + // A saveChanges() with a delete of new record would result in an empty change set + // An empty DataSet is sent to the server to ensure that AfterSaveChanges fires + var keys = Object.keys(changeSetJsonObject[dataSetName]); + if (keys.length == 1 && keys[0] == "prods:hasChanges") { + for (var buf in this._buffers) { + dataSetJsonObject[this._buffers[buf]._name] = []; + } + dataSetJsonObject["prods:hasChanges"] = false; + } + + return changeSetJsonObject; + }; + + /************************************************************************ + * + * Private method that creates a jsonObject for the created and changed records + * in a temp-table. There is no before-image information. This is used in the + * case of a Submit operation when the JSDO is just for a temp-table + * + * Params: dataSetName is required. + * alwaysCreateTable is required. If true, always create table array (even if no data/changes) + * request is optional + */ + this._createTTChangeSet = function (tableRef, request) { + var changeSetJsonObject = {}, + hasChanges, + tempTableJsonObject, + i, + id, + jsrecord; + + changeSetJsonObject[tableRef._name] = []; + tempTableJsonObject = changeSetJsonObject[tableRef._name]; + + hasChanges = this._hasChanges(); + if (hasChanges) { + + // (note that we do not send deleted rows on submit for a temp-table) + + // Adds + for (i = 0; i < tableRef._added.length; i++) { + id = tableRef._added[i]; + jsrecord = tableRef._findById(id, false); + if (jsrecord) { + if ( !tableRef._processed[jsrecord.data._id] ) { + this._addRowToTTChangeSet(tableRef, jsrecord, tempTableJsonObject, + request, "beforeCreate"); + } + } + } + + // changed rows + for (id in tableRef._changed) { + if (tableRef._changed.hasOwnProperty(id)) { + jsrecord = tableRef._findById(id, false); + if (jsrecord) { + if ( !tableRef._processed[jsrecord.data._id] ) { + this._addRowToTTChangeSet(tableRef, jsrecord, tempTableJsonObject, + request, "beforeUpdate"); + } + } + } + } + + // Clear _processed map + tableRef._processed = {}; + } + + return changeSetJsonObject; + }; + + this._addRowToTTChangeSet = function (tableRef, jsrecord, tempTableJsonObject, request, event) { + var rowData = {}; + + tableRef._processed[jsrecord.data._id] = jsrecord.data; + + // Store jsrecord in request object so we can access it when saveChanges completes, + // in order to run afterCreate events + if (typeof(request) != 'undefined') { + request.jsrecords.push(jsrecord); + + // Need to call beforeCreate trigger when saveChanges(true) is called + jsrecord._tableRef.trigger(event, this, jsrecord, request); + this.trigger(event, this, jsrecord, request); + } + + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + delete rowData["_id"]; + + tempTableJsonObject.push(rowData); + }; + + /************************************************************************ + * + * Private method that creates a jsonObject with data and also before image data + * for all records in change-set (creates, updates and deletes) + * + * Params: dataSetName is required. + * It returns jsonObject that can be used as input to addRecords() + */ + this._createDataAndChangeSet = function (dataSetName) { + var jsonObject = {}; + + jsonObject[dataSetName] = {}; + var dataSetJsonObject = jsonObject[dataSetName]; + + /* We always want to create tables (even if there's no data) so we can compare schemas + * of data in local storage to JSDO's schema */ + for (var buf in this._buffers) + dataSetJsonObject[this._buffers[buf]._name] = []; + + if (this._hasChanges()) { + dataSetJsonObject["prods:hasChanges"] = true; + } + + // Add data from each table. This will also add bi data for any created or updated rows + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._addRecordsToObject(tableRef, dataSetJsonObject); + } + + // Now do deletes + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._addDeletesToChangeSet(tableRef, dataSetJsonObject); + } + + // Clear _processed map + for (var buf in this._buffers) { + this._buffers[buf]._processed = {}; + } + return jsonObject; + }; + + // This method adds all record for specified table into dataSetJsonObject. + // If record has bi data, it adds that as well + this._addRecordsToObject = function (tableRef, dataSetJsonObject) { + + if (tableRef._data.length > 0 && !dataSetJsonObject[tableRef._name]) + dataSetJsonObject[tableRef._name] = []; + + for (var i = 0; i < tableRef._data.length; i++) { + var record = tableRef._data[i]; + if (!record) continue; + + // Check if record has bi data, can only determine if it's created or changed since + // deleted rows are not in after data + if (this._doesRecordHaveCreateBIData(tableRef, record._id) === true) { + var jsrecord = tableRef._findById(record._id, false); + if (!jsrecord) continue; + if (tableRef._processed[jsrecord.data._id]) continue; + this._addCreatedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject); + } + if (this._doesRecordHaveUpdateBIData(tableRef, record._id) === true) { + var jsrecord = tableRef._findById(record._id, false); + if (!jsrecord) continue; + if (tableRef._processed[jsrecord.data._id]) continue; + this._addChangedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject); + } + else { + if (tableRef._processed[record._id]) continue; + tableRef._processed[record._id] = record; + + var rowData = {}; + + tableRef._jsdo._copyRecord(tableRef, record, rowData); + delete rowData["_id"]; + + dataSetJsonObject[tableRef._name].push(rowData); + } + } + }; + + + // Check if specified after record has bi data for newly created record. + // Returns True if after record has corresponding bi data, else false + this._doesRecordHaveCreateBIData = function (tableRef, id) { + for (var i = 0; i < tableRef._added.length; i++) { + if (tableRef._added[i] === id) + return true; + } + + return false; + }; + + // Check if specified after record has bi data for updated record. + // Returns True if after record has corresponding bi data, else false + this._doesRecordHaveUpdateBIData = function (tableRef, id) { + for (var changedId in tableRef._changed) { + if (changedId === id) + return true; + } + + return false; + }; + + + // If a create, remove or update exists, method returns true, else returns false + this._hasChanges = function () { + var hasChanges = false; + + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + + var hasUpdates = false; + for (var id in tableRef._changed) { + hasUpdates = true; + break; + } + + if (tableRef._deleted.length > 0 || tableRef._added.length > 0 || hasUpdates) { + hasChanges = true; + break; + } + } + + return hasChanges; + }; + + // This method is used when saveChanges() is called, and also when storing data to local storage. + // The request param should be defined for saveChanges(), + // but not needed when storing data to local storage + this._addDeletesToChangeSet = function (tableRef, dataSetJsonObject, request) { + // There is no after table for deletes, so just create before-table data + for (var i = 0; i < tableRef._deleted.length; i++) { + var jsrecord = tableRef._deleted[i]; + + if (!jsrecord) continue; + + if (jsrecord.data + && jsrecord.data._id !== undefined + && tableRef._beforeImage[jsrecord.data._id] === null) { + // Deleted record is for a new record - do not send deleted record to server + continue; + } + + this._addDeletedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); + } + }; + + this._addDeletedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { + tableRef._processed[jsrecord.data._id] = jsrecord.data; + + // Store jsrecord in request object so we can access it when saveChanges completes, + // in order to run afterDelete events + jsrecord.data["prods:rowState"] = "deleted"; + + if (typeof(request) != 'undefined') { + request.jsrecords.push(jsrecord); + + // Need to call beforeDelete trigger if saveChanges(true) is called + jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); + this.trigger("beforeDelete", this, jsrecord, request); + } + + var beforeRowData = {}; + // AppServer will roundtrip this back to jsdo client + beforeRowData["prods:clientId"] = jsrecord.data._id; + beforeRowData["prods:rowState"] = "deleted"; + + var beforeTableJsonObject = this._getTableInBeforeJsonObject(dataSetJsonObject, tableRef._name); + tableRef._jsdo._copyRecord(tableRef, tableRef._beforeImage[jsrecord.data._id], beforeRowData); + delete beforeRowData["_id"]; + + beforeTableJsonObject.push(beforeRowData); + }; + + // This method is used when saveChanges() is called, and also when storing data to local storage. + // The request param should be defined for saveChanges(), + // but not needed when storing data to local storage + this._addCreatesToChangeSet = function (tableRef, dataSetJsonObject, request) { + // There is no before table for creates, so just create after-table data + for (var i = 0; i < tableRef._added.length; i++) { + var id = tableRef._added[i]; + var jsrecord = tableRef._findById(id, false); + if (!jsrecord) continue; + if (tableRef._processed[jsrecord.data._id]) continue; + + this._addCreatedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); + } + }; + + this._addCreatedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { + tableRef._processed[jsrecord.data._id] = jsrecord.data; + + if (!dataSetJsonObject[tableRef._name]) { + dataSetJsonObject[tableRef._name] = []; + } + + // Store jsrecord in request object so we can access it when saveChanges completes, + // in order to run afterCreate events + jsrecord.data["prods:rowState"] = "created"; + + if (typeof(request) != 'undefined') { + request.jsrecords.push(jsrecord); + + // Need to call beforeCreate trigger when saveChanges(true) is called + jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); + this.trigger("beforeCreate", this, jsrecord, request); + } + + var rowData = {}; + // AppServer will roundtrip this back to jsdo client + rowData["prods:clientId"] = jsrecord.data._id; + rowData["prods:rowState"] = "created"; + + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + delete rowData["_id"]; + + dataSetJsonObject[tableRef._name].push(rowData); + }; + + // This method is used when saveChanges() is called, and also when storing data to local storage. + // The request param should be defined for saveChanges(), + // but not needed when storing data to local storage + this._addChangesToChangeSet = function (tableRef, dataSetJsonObject, request) { + // For Changes, there is both before and after table data + for (var id in tableRef._changed) { + var jsrecord = tableRef._findById(id, false); + if (!jsrecord) continue; + if (tableRef._processed[jsrecord.data._id]) continue; + + this._addChangedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); + } + }; + + this._addChangedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { + tableRef._processed[jsrecord.data._id] = jsrecord.data; + + if (!dataSetJsonObject[tableRef._name]) { + dataSetJsonObject[tableRef._name] = []; + } + + // Store jsrecord in request object so we can access it when saveChanges completes, in order + // to run afterUpdate events + jsrecord.data["prods:rowState"] = "modified"; + + if (typeof(request) != 'undefined') { + request.jsrecords.push(jsrecord); + + // Need to call beforeUpdate trigger when saveChanges(true) is called + jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); + this.trigger("beforeUpdate", this, jsrecord, request); + } + + var rowData = {}; + // Required by AppServer in before-image data. Matches before row + rowData["prods:id"] = jsrecord.data._id; + // AppServer will roundtrip this back to jsdo client + rowData["prods:clientId"] = jsrecord.data._id; + rowData["prods:rowState"] = "modified"; + + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + delete rowData["_id"]; + + dataSetJsonObject[tableRef._name].push(rowData); + + // Now add before-image data + var beforeTableJsonObject = this._getTableInBeforeJsonObject(dataSetJsonObject, tableRef._name); + var beforeRowData = {}; + // Required by AppServer in before-image data. Matches after row + beforeRowData["prods:id"] = jsrecord.data._id; + + tableRef._jsdo._copyRecord(tableRef, tableRef._beforeImage[jsrecord.data._id], beforeRowData); + //delete beforeRowData["_id"]; + + beforeTableJsonObject.push(beforeRowData); + }; + + + // Private method to get table's json object from the specified dataset json object. + // If it hasn't been created yet, this method creates it. + this._getTableInBeforeJsonObject = function (dataSetJsonObject, tableName) { + if (!dataSetJsonObject["prods:before"]) { + dataSetJsonObject["prods:before"] = {}; + } + var beforeObject = dataSetJsonObject["prods:before"]; + + if (!beforeObject[tableName]) { + beforeObject[tableName] = []; + } + + return beforeObject[tableName]; + }; + + + /********************************************************************* + * + * Reads a JSON object into the JSDO memory. + */ + this.addRecords = function (jsonObject, addMode, keyFields, trackChanges, isInvoke) { + if (this.isDataSet()) { + if (jsonObject instanceof Array) { + if (!this._defaultTableRef) { + throw new Error(msg.getMsgText("jsdoMSG998")); + } + } + else { + if (jsonObject === undefined || jsonObject === null) { + jsonObject = {}; + } + + if (jsonObject[this._dataSetName]) { + jsonObject = jsonObject[this._dataSetName]; + } + } + + // Allow empty object in addRecords with MODE_EMPTY + if (addMode != progress.data.JSDO.MODE_EMPTY) { + if (Object.keys(jsonObject).length === 0) + throw new Error(msg.getMsgText("jsdoMSG006")); + } + + var oldUseRelationships = this.useRelationships; + // Turn off useRelationships since addRecords() does not use the working record + this.useRelationships = false; + try { + for (var buf in this._buffers) { + // Read data for tables in JSON object + if (jsonObject[this._buffers[buf]._name]) + this._addRecords(this._buffers[buf]._name, jsonObject, addMode, + keyFields, trackChanges, isInvoke); + else if (addMode == progress.data.JSDO.MODE_EMPTY) { + this._buffers[this._buffers[buf]._name]._clearData(); + } + } + } finally { + // Restore useRelationships + this.useRelationships = oldUseRelationships; + } + } + else if (this._defaultTableRef) { + this._addRecords(this._defaultTableRef._name, jsonObject, addMode, keyFields, + trackChanges, isInvoke); + } + }; + + /* + * Copies the fields of the source record to the target record. + * Preserves the _id of the target record. + */ + this._copyRecord = function (tableRef, source, target, onlyChangesRecord) { + for (var field in source) { + + if (onlyChangesRecord !== undefined) { + if (source[field] == onlyChangesRecord[field]) + continue; + } + + // Fix for PSC00277769 + if (source[field] === undefined || source[field] === null) { + target[field] = source[field]; + } + else if (source[field] instanceof Date) { + target[field] = source[field]; + } + else if (typeof source[field] === 'object') { + var newObject = source[field] instanceof Array ? [] : {}; + this._copyRecord(tableRef, source[field], newObject); + target[field] = newObject; + } + else + target[field] = source[field]; + } + }; + + /* + * Deletes the "prods:" properties when no longer needed, + * typically when doing acceptChanges, rejectChanges, or _applyChanges. + * These properties are used to transfer before-image info between client JSDO and AppServer. + * + * Also, it optionally clears out the errorString field depending upon value of clearErrorString. + * To be consistent with the handling of + * the ABL's Buffer ERROR-STRING attribute, + * the errorString field should be cleared out when doing acceptChanges() or rejectChanges(). + */ + this._deleteProdsProperties = function (record, clearErrorString, deleteRowState) { + + /* Default to false */ + if (typeof(clearErrorString) == 'undefined') { + clearErrorString = false; + } + + /* Default to true */ + if (typeof(deleteRowState) == 'undefined') { + deleteRowState = true; + } + + if (record) { + delete record["prods:id"]; + delete record["prods:hasErrors"]; + delete record["prods:clientId"]; + delete record["prods:rejected"]; + delete record._rejected; + + if (deleteRowState) { + delete record["prods:rowState"]; + } + + if (clearErrorString) { + delete record._errorString; + } + } + }; + + this._addRecords = function (tableName, jsonObject, addMode, keyFields, trackChanges, isInvoke) { + var beforeImageJsonObject = null; + var beforeImageJsonIndex = null; + + if (jsonObject && (this._dataSetName !== undefined)) { + if (jsonObject[this._dataSetName] && + jsonObject[this._dataSetName]["prods:hasChanges"]) { + beforeImageJsonObject = jsonObject; + beforeImageJsonIndex = {}; + } + else if (jsonObject["prods:hasChanges"]) { + beforeImageJsonObject = {}; + beforeImageJsonObject[this._dataSetName] = jsonObject; + beforeImageJsonIndex = {}; + } + } + + if (typeof(tableName) != 'string') + throw new Error(msg.getMsgText("jsdoMSG020")); + if (!addMode) + throw new Error(msg.getMsgText("jsdoMSG021")); + + switch (addMode) { + case progress.data.JSDO.MODE_APPEND: + case progress.data.JSDO.MODE_EMPTY: + case progress.data.JSDO.MODE_MERGE: + case progress.data.JSDO.MODE_REPLACE: + break; + default: + throw new Error(msg.getMsgText("jsdoMSG022")); + } + + if (!keyFields) + keyFields = []; + else { + if (!(keyFields instanceof Array) && (typeof(keyFields) == 'object')) { + if (keyFields[tableName]) { + keyFields = keyFields[tableName]; + } + else { + keyFields = []; + } + } + } + + if (!(keyFields instanceof Array)) { + throw new Error(msg.getMsgText("jsdoMSG008")); + } + + // Check that the specified field names are in the schema + if (this._buffers[tableName]._fields) { + for (var i = 0; i < keyFields.length; i++) { + var field = this._buffers[tableName]._fields[keyFields[i].toLowerCase()]; + if (field === undefined) { + throw new Error(msg.getMsgText("jsdoMSG009", keyFields[i])); + } + else { + keyFields[i] = field.name; + } + } + } + + trackChanges = trackChanges ? true : false; + + if (tableName) { + if (!(jsonObject instanceof Array)) { + var data = null; + + if (jsonObject === undefined || jsonObject === null) { + jsonObject = {}; + } + + if (this.isDataSet()) { + if (jsonObject[this._dataSetName]) + data = jsonObject[this._dataSetName][tableName]; + else if (jsonObject[tableName]) + data = jsonObject[tableName]; + } else { + if (this._dataProperty) + data = jsonObject[this._dataProperty]; + else if (jsonObject.data) + data = jsonObject.data; + } + + + if (data instanceof Array) { + saveJsonObject = jsonObject; + jsonObject = data; + } + else if ((addMode == progress.data.JSDO.MODE_EMPTY) + && (typeof (jsonObject) == 'object') + && (Object.keys(jsonObject).length === 0)) { + jsonObject = []; // Allow empty object in addRecords with + // MODE_EMPTY + } + // Allow empty object when called by restoreChangesOnlyForTable() + // where there are only deletes - in bi data + else if ((addMode == progress.data.JSDO.MODE_REPLACE) + && (typeof (jsonObject) == 'object') + && (beforeImageJsonObject)) { + jsonObject = []; + } + } + + if (!(jsonObject instanceof Array)) { + throw new Error(msg.getMsgText("jsdoMSG005", tableName)); + } + + var dataHasBeenProcessed = false; + try { + this._buffers[tableName]._sortRecords = false; + if (keyFields.length === 0 || addMode == progress.data.JSDO.MODE_EMPTY) { + // Quick merge + if (addMode == progress.data.JSDO.MODE_EMPTY) { + this._buffers[tableName]._clearData(); + } + // APPEND, MERGE, REPLACE + for (var i = 0; i < jsonObject.length; i++) { + var jsrecord = this._buffers[tableName]._add(jsonObject[i], trackChanges, false); + jsonObject[i]._id = jsrecord.data._id; + if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { + beforeImageJsonIndex[jsonObject[i]["prods:id"]] = jsrecord.data._id; + } + if (beforeImageJsonObject) { + this._deleteProdsProperties(jsrecord.data); + } + } + } + else { + // Build temporary index + var tmpIndex; + + if (this._buffers[tableName]._data.length * jsonObject.length >= 10) { + tmpIndex = {}; + + for (var i = 0; i < this._buffers[tableName]._data.length; i++) { + var record = this._buffers[tableName]._data[i]; + if (!record) continue; + + var key = this._buffers[tableName]._getKey(record, keyFields); + tmpIndex[key] = record; + } + + } + else + tmpIndex = null; // Do not use an index + var checkBeforeImage = + (Object.keys(this._buffers[tableName]._beforeImage).length !== 0); + for (var i = 0; i < jsonObject.length; i++) { + var match = false; + var record = null; + + // Check for duplicates + if (tmpIndex) { + var key = this._buffers[tableName]._getKey(jsonObject[i], keyFields); + record = tmpIndex[key]; + match = (record !== undefined); + } + else { + for (var j = 0; j < this._buffers[tableName]._data.length; j++) { + record = this._buffers[tableName]._data[j]; + if (!record) continue; + match = + (this._buffers[tableName]._equalRecord(jsonObject[i], record, keyFields)); + if (match) { + // Duplicate found + break; + } + } + } + + if (match) { + if (isInvoke + && (this._resource.idProperty !== undefined) + && (jsonObject[i]._id === undefined)) { + // Add _id to jsonObject + jsonObject[i]._id = record._id; + } + + // If beforeRecord is null, there is entry in _beforeImage for a create. + // If beforeRecord is undefined, there is no entry + var beforeRecord = this._buffers[tableName]._beforeImage[record._id]; + if (checkBeforeImage + && (jsonObject[i]["prods:id"] !== undefined) + && (typeof beforeRecord !== 'undefined')) { + // Only throw exception if the existing bi data + // is not the same as the new bi data + var isAfterSame = this._sameData(jsonObject[i], record); + var isBeforeSame = true; + + // For creates, beforeRecord will be null + if (beforeRecord) { + var beforeObject = this._getBeforeRecordFromObject(jsonObject[i], + beforeImageJsonObject, tableName); + if (beforeObject) + isBeforeSame = this._sameData(beforeObject, beforeRecord); + } + + if (!isAfterSame || !isBeforeSame) + throw new Error(msg.getMsgText("jsdoMSG032")); + } + + switch (addMode) { + case progress.data.JSDO.MODE_APPEND: + throw new Error(msg.getMsgText("jsdoMSG023")); + case progress.data.JSDO.MODE_MERGE: + /* Ignore duplicate */ + if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { + beforeImageJsonIndex[jsonObject[i]["prods:id"]] = record._id; + } + break; + case progress.data.JSDO.MODE_REPLACE: + if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { + beforeImageJsonIndex[jsonObject[i]["prods:id"]] = record._id; + } + + if (jsonObject[i]._id === undefined) + jsonObject[i]._id = record._id; + this._copyRecord( + this._buffers[tableName], + jsonObject[i], record); + this._deleteProdsProperties(record); + break; + default: + break; + } + } + else { + // Add record + var jsrecord = + this._buffers[tableName]._add(jsonObject[i], trackChanges, false); + jsonObject[i]._id = jsrecord.data._id; + if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { + beforeImageJsonIndex[jsonObject[i]["prods:id"]] = jsrecord.data._id; + } + if (beforeImageJsonObject) { + this._deleteProdsProperties(jsrecord.data); + } + if (tmpIndex) { + var key = this._buffers[tableName]._getKey(jsrecord.data, keyFields); + tmpIndex[key] = jsrecord.data; + } + } + + } + tmpIndex = null; + } + dataHasBeenProcessed = true; + } + finally { + this._buffers[tableName]._sortRecords = true; + this._buffers[tableName]._sort(); + this._buffers[tableName]._createIndex(); + + if (dataHasBeenProcessed && beforeImageJsonObject) { + this._buffers[tableName]._loadBeforeImageData(beforeImageJsonObject, + beforeImageJsonIndex, keyFields); + } + } + } + }; + + // This method returns corresponding bi record of the afterRecord from specified jsonObject + this._getBeforeRecordFromObject = function (afterRecord, jsonObject, tablename) { + var beforeData = jsonObject[this._dataSetName]["prods:before"]; + var id = afterRecord["prods:id"]; + var beforeRecord; + + if (!beforeData) return beforeRecord; + + // First check to see if the before data is the same + for (var i = 0; i < beforeData[tablename].length; i++) { + var record = beforeData[tablename][i]; + if (record["prods:id"] && id == record["prods:id"]) { + beforeRecord = record; + break; + } + } + + return beforeRecord; + }; + + this._sameData = function (record1, record2) { + var value1, value2; + for (var fieldName in record1) { + if (fieldName.substring(0, 5) != "prods" && fieldName != "_id") { + value1 = record1[fieldName]; + value2 = record2[fieldName]; + + if (value1 > value2 || value1 === null) + return false; + else if (value1 < value2 || value2 === null) + return false; + } + } + + return true; + }; + + + // private method to merge changes after a read operation + this._mergeRead = function (jsonObject, xhr) { + if (this.isDataSet()) { + if (this._dataProperty) { + var datasetBuffer = this._buffers[this._dataProperty]; + datasetBuffer._data = jsonObject[this._dataSetName][this._dataProperty]; + if (datasetBuffer.autoSort) { + datasetBuffer._sort(); + } + datasetBuffer._createIndex(); + } + else { + // Load data from JSON object into _data + for (var buf in this._buffers) { + var data; + if (jsonObject[this._dataSetName]) + data = jsonObject[this._dataSetName][buf]; + else + data = null; + data = data ? data : []; + this._buffers[buf]._data = data; + if (this._buffers[buf].autoSort) { + this._buffers[buf]._sort(); + } + this._buffers[buf]._createIndex(); + if (jsonObject[this._dataSetName] + && jsonObject[this._dataSetName]["prods:hasChanges"]) { + this._buffers[buf]._loadBeforeImageData(jsonObject); + } + } + // Load nested data into _data + if (this._numBuffers > 1) { + for (var buf in this._buffers) { + if (this._buffers[buf]._isNested + && this._buffers[buf]._parent + && this._buffers[this._buffers[buf]._parent]) { + var srcData = this._buffers[this._buffers[buf]._parent]._data; + var data = []; + for (var i = 0; i < srcData.length; i++) { + if (srcData[i][buf] !== undefined) { + for (var j = 0; j < srcData[i][buf].length; j++) { + data.push(srcData[i][buf][j]); + } + delete srcData[i][buf]; + } + } + this._buffers[buf]._data = data; + if (this._buffers[buf].autoSort) { + this._buffers[buf]._sort(); + } + this._buffers[buf]._createIndex(); + } + } + } + } + } + else { + if (jsonObject instanceof Array) { + this._defaultTableRef._data = jsonObject; + } + else { + if (this._dataProperty) + this._defaultTableRef._data = jsonObject[this._dataProperty]; + else if (jsonObject.data) + this._defaultTableRef._data = jsonObject.data; + else { + this._defaultTableRef._data = []; + this._defaultTableRef._data[0] = jsonObject; + } + } + } + + for (var buf in this._buffers) { + if (this._buffers[buf].autoSort) { + this._buffers[buf]._sort(); + } + this._buffers[buf]._createIndex(); + } + }; + + /** + * Replace existing record data and index entry with new record data. + */ + this._mergeUpdateRecord = function (tableRef, recordId, record) { + var index = tableRef._index[recordId].index; + record._id = recordId; + + if (!tableRef._data[index]) { + tableRef._data[index] = {}; + } + this._copyRecord(this._tableRef, record, tableRef._data[index]); + record = tableRef._data[index]; + + if (tableRef._jsdo._resource.idProperty !== undefined) { + var id = tableRef._data[index][tableRef._jsdo._resource.idProperty]; + if (id !== undefined) { + delete tableRef._index[recordId]; + id += ""; + tableRef._index[id] = new progress.data.JSIndexEntry(index); + record._id = id; + } + } + + return record; + }; + + + /** + *update existing record data with specified error string + */ + this._setErrorString = function (tableRef, recordId, errorString, setInBeforeTable) { + + if (setInBeforeTable) { + // Ensure that object exists, it's null for deleted rows + if (tableRef._beforeImage[recordId]) { + tableRef._beforeImage[recordId]._errorString = errorString; + } + } + else { + var index = tableRef._index[recordId].index; + tableRef._data[index]._errorString = errorString; + } + }; + + /* + * Returns the array with the data from the specified dataObject. + */ + this._arrayFromDataObject = function (dataObject, tableRef) { + var data; + + if (dataObject === undefined) return undefined; + if (this._dataSetName) { + if (dataObject[this._dataSetName]) + data = dataObject[this._dataSetName][tableRef._name]; + } + else { + // check if data returned as array + if (dataObject instanceof Array) { + data = dataObject; + } else { + // or if data property is set + if (this._dataProperty) { + data = dataObject[this._dataProperty]; + } else if (dataObject.data) { + // or just try with 'data' as the data property name + data = dataObject.data; + } + } + } + + return data; + }; + + ///////////////////////////////////////////////////////////////////////////////////////////// + // + // Private method to merge changes after a create or update operation. + // This method is called to merge changes when server's Create or Update methods were called. + // + // It returns true if it found error for row in before-image data (prods:hasErrors = true) + // It returns false if there is no before-image data or prods:hasErrors property is absent + this._mergeUpdateForCUD = function (jsonObject, xhr) { + var hasError = false, + errorString; + + // Update dataset with changes from server + if (this._dataSetName) { + var dataSetJsonObject = jsonObject[this._dataSetName]; + + // only updates the specified record + var tableRef = xhr.request.jsrecord._tableRef; + var tableJsonObject = this._arrayFromDataObject(jsonObject, tableRef); + + if (tableJsonObject instanceof Array) { + if (tableJsonObject.length > 1) { + xhr.request.success = false; + throw new Error(msg.getMsgText("jsdoMSG100")); + } + + for (var i = 0; i < tableJsonObject.length; i++) { + var recordId = xhr.request.jsrecord.getId(); + + if (!recordId) { + throw new Error(msg.getMsgText("jsdoMSG034", "_mergeUpdateForCUD()")); + } + + // Determine if error string (get prods_id before _mergeUpdateRecord() is called, + // since it removes all prods properties) + errorString = undefined; + + if (tableJsonObject[i]["prods:hasErrors"]) { + var prods_id = tableJsonObject[i]["prods:id"]; + errorString = + this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); + hasError = true; + } + + var record = this._mergeUpdateRecord(tableRef, recordId, tableJsonObject[i]); + if (errorString) + this._setErrorString(tableRef, recordId, errorString, false); + + // Set _rejected property + if (tableJsonObject[i]["prods:rejected"] + || errorString) { + record._rejected = true; + if (errorString === "REJECTED") { + delete record._errorString; + } + } + + xhr.request.jsrecord = new progress.data.JSRecord(tableRef, record); + } + } + } else { + // update single record with changes from server + var tableRef = this._defaultTableRef; + var data = this._arrayFromDataObject(jsonObject); + + if (data instanceof Array) { + if (data.length > 1) { + xhr.request.success = false; + throw new Error(msg.getMsgText("jsdoMSG100")); + } + + for (var i = 0; i < data.length; i++) { + var recordId = xhr.request.jsrecord.getId(); + + if (!recordId) { + throw new Error(msg.getMsgText("jsdoMSG034", "_mergeUpdateForCUD()")); + } + + var record = this._mergeUpdateRecord(tableRef, recordId, data[i]); + xhr.request.jsrecord = new progress.data.JSRecord(tableRef, record); + } + } + } + + return hasError; + }; + + + ///////////////////////////////////////////////////////////////////////////////////////////// + // + // Private method to determine if deleted row (from delete operation) returned from AppServer + // was returned with an error in the before-image data. + // + // It returns true if it found an error for row in before-image data (prods:hasErrors = true) + // It returns false if there is no before-image data or prods:hasErrors property is absent + + this._checkForDeleteError = function (dataSetJsonObject, xhr) { + var hasError = false; + var tableRef = xhr.request.jsrecord._tableRef; + + beforeJsonObject = dataSetJsonObject["prods:before"]; + + // No merge is necessary for deletes, but we need to see + // if there are any errors on deletes records. + // delete records are not in after table, only in before table + if (beforeJsonObject) { + var beforeTableJsonObject = beforeJsonObject[tableRef._name]; + + if (beforeTableJsonObject.length > 1) { + xhr.request.success = false; + throw new Error(msg.getMsgText("jsdoMSG100")); + } + // clientId is same as _id + var recordId = beforeTableJsonObject[0]["prods:clientId"]; + if (!recordId) { + throw new Error(msg.getMsgText("jsdoMSG035", "_checkForDeleteError()")); + } + + // Determine if row was returned with error string + if (beforeTableJsonObject[0]["prods:hasErrors"]) { + var prods_id = beforeTableJsonObject[0]["prods:id"]; + var errorString = + this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); + this._setErrorString(tableRef, recordId, errorString, true); + hasError = true; + } + } + + return hasError; + }; + + ///////////////////////////////////////////////////////////////////////////////////////////// + // + // Private method to merge changes after a call to saveChanges. + // This method is called when saveChanges(useSubmit) was called with useSubmit=true. + // This can process/merge one or more created, deleted or updated records. + // In order for a jsonObject to have before-image data, it must be associated with a dataset. + // + // It only merges changes in the after table. But we need to look at before-image table to see + // if there were any errors passed back for the deletes + // + this._mergeUpdateForSubmit = function (jsonObject, xhr) { + var errorString; + + //if (!this._dataSetName || !jsonObject[this._dataSetName]["prods:hasChanges"]) + if (!this._dataSetName) { + // "_mergeUpdateForSubmit() can only be called for a dataset" + throw new Error(msg.getMsgText("jsdoMSG036", "_mergeUpdateForSubmit()")); + } + + // response is sent back with extra dataset object wrapper + var dataSetJsonObject = jsonObject[this._dataSetName]; + if (dataSetJsonObject[this._dataSetName]) + dataSetJsonObject = dataSetJsonObject[this._dataSetName]; + + var beforeJsonObject = dataSetJsonObject["prods:before"]; + + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + + var tableJsonObject = dataSetJsonObject[tableRef._name]; + if (tableJsonObject instanceof Array) { + for (var i = 0; i < tableJsonObject.length; i++) { + + var recordId = tableJsonObject[i]["prods:clientId"]; + if (!recordId) { + throw new Error(msg.getMsgText("jsdoMSG035", "_mergeUpdateForSubmit()")); + } + + // Determine if error string (get prods_id before _mergeUpdateRecord() is called, + // since it removes all prods properties) + errorString = undefined; + + if (tableJsonObject[i]["prods:hasErrors"]) { + var prods_id = tableJsonObject[i]["prods:id"]; + errorString = + this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); + } + var record = this._mergeUpdateRecord(tableRef, recordId, tableJsonObject[i]); + if (errorString) { + this._setErrorString(tableRef, recordId, errorString, false); + } + + // Set _rejected property so it can be checked in applyChanges() + if (tableJsonObject[i]["prods:rejected"] + || errorString) { + record._rejected = true; + if (errorString === "REJECTED") { + delete record._errorString; + } + } + + // Now need to update jsrecords. + // We use this data when we fire create, update and delete events. + // Updating so that it contains latest data (data sent back from server) + var jsrecords = xhr.request.jsrecords; + for (var idx = 0; idx < jsrecords.length; idx++) { + if (jsrecords[idx].data["_id"] == recordId) { + jsrecords[idx].data = record; + break; + } + } + } + } + } + + // No merge is necessary for deletes, + // but we need to see if there are any errors on deletes records. + // delete records are not in after table, only in before table + if (beforeJsonObject) { + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + var beforeTableJsonObject = beforeJsonObject[tableRef._name]; + var errorString; + + if (beforeTableJsonObject instanceof Array) { + for (var i = 0; i < beforeTableJsonObject.length; i++) { + + if (beforeTableJsonObject[i]["prods:rowState"] == "deleted") { + var recordId = beforeTableJsonObject[i]["prods:clientId"]; + if (!recordId) { + throw new Error(msg.getMsgText("jsdoMSG035", "_mergeUpdateForSubmit()")); + } + + errorString = undefined; + // If row was returned with error string, just copy that over to jsdo record + if (beforeTableJsonObject[i]["prods:hasErrors"]) { + var prods_id = beforeTableJsonObject[i]["prods:id"]; + + errorString = this._getErrorStringFromJsonObject(dataSetJsonObject, + tableRef, prods_id); + this._setErrorString(tableRef, recordId, errorString, true); + } + + // Set _rejected property so it can be checked in applyChanges() + if ((beforeTableJsonObject[i]["prods:rejected"] + || errorString) + && tableRef._beforeImage[recordId]) { + tableRef._beforeImage[recordId]._rejected = true; + if (errorString === "REJECTED") { + delete tableRef._beforeImage[recordId]._errorString; + } + } + } + } + } + } + } + }; + + ///////////////////////////////////////////////////////////////////////////////////////////// + // + // Private method that fires afterCreate, afterUpdate and afterDelete (CUD) triggers after + // saveChanges(true) is called. We must fire create, update and delete triggers + // for each record that was sent to backend submit operation + this._fireCUDTriggersForSubmit = function (request) { + // Before firing triggers, delete prods properties (except rowState) so they don't appear in data + for (var idx = 0; idx < request.jsrecords.length; idx++) { + this._deleteProdsProperties(request.jsrecords[idx].data, false, false); + } + + for (var idx = 0; idx < request.jsrecords.length; idx++) { + var jsrecord = request.jsrecords[idx]; + switch (jsrecord.data["prods:rowState"]) { + case "created": + jsrecord._tableRef.trigger("afterCreate", this, jsrecord, request.success, request); + this.trigger("afterCreate", this, jsrecord, request.success, request); + break; + case "modified": + jsrecord._tableRef.trigger("afterUpdate", this, jsrecord, request.success, request); + this.trigger("afterUpdate", this, jsrecord, request.success, request); + break; + case "deleted": + jsrecord._tableRef.trigger("afterDelete", this, jsrecord, request.success, request); + this.trigger("afterDelete", this, jsrecord, request.success, request); + break; + } + } + }; + + ////////////////////////////////////////////////////////////////////////////////////////////// + // + // Private method to return error for specified row + // from jsonObject's prods:errors object (before-data) sent over from AppServer + // + this._getErrorStringFromJsonObject = function (dataSetJsonObject, tableRef, prods_id) { + var tableJsonObject; + var errorsJsonObject = dataSetJsonObject["prods:errors"]; + + if (errorsJsonObject) { + tableJsonObject = errorsJsonObject[tableRef._name]; + } + + if (tableJsonObject instanceof Array) { + for (var i = 0; i < tableJsonObject.length; i++) { + + var id = tableJsonObject[i]["prods:id"]; + if (id === prods_id) { + var errorString = tableJsonObject[i]["prods:error"]; + return errorString === null ? + "Server returned unspecified error. Please check log files." : errorString; + } + } + } + + return undefined; + }; + + this._fillSuccess = function (jsdo, success, request) { + var xhr = request.xhr, + properties; + + // Need to check if responseMapping was specified; developer can specify + // plug-in to manipulate response + properties = jsdo.getMethodProperties("read"); + + if (properties && properties.mappingType) { + mapping = progress.data.PluginManager.getPlugin(properties.mappingType); + if (!mapping) { + throw new Error(progress.data._getMsgText("jsdoMSG118", properties.mappingType)); + } + + if (typeof (mapping.responseMapping) === "function") { + request.response = mapping.responseMapping(jsdo, request.response, { operation: "read" }); + } + } + + jsdo._clearData(); + jsdo._mergeRead(request.response, xhr); + + // Set working record + for (var buf in jsdo._buffers) { + if (!jsdo._buffers[buf]._parent || !jsdo.useRelationships) { + jsdo._buffers[buf]._setRecord(jsdo._buffers[buf]._findFirst()); + } + } + }; + + this._fillComplete = function (jsdo, success, request) { + jsdo.trigger("afterFill", jsdo, request.success, request); + if (request.deferred) { + if (success) { + request.deferred.resolve(jsdo, success, request); + } + else { + request.deferred.reject(jsdo, success, request); + } + } + }; + + this._fillError = function (jsdo, success, request) { + jsdo._clearData(); + jsdo._updateLastErrors(jsdo, null, null, request); + }; + + this._undoCreate = function (tableRef, id) { + // Undo operation + // Remove record from JSDO memory + var entry = tableRef._index[id]; + if (entry !== undefined) { + var index = entry.index; + tableRef._data[index] = null; + } + tableRef._hasEmptyBlocks = true; + delete tableRef._index[id]; + delete tableRef._beforeImage[id]; + // End - Undo operation + }; + + this._undoUpdate = function (tableRef, id, deleteProdsProps) { + /* Default to false */ + if (typeof(deleteProdsProps) == 'undefined') { + deleteProdsProps = false; + } + + // Undo operation + // Restore from before image + var record = tableRef._beforeImage[id]; + + // Before image points to an existing record + if (record) { + var index = tableRef._index[id].index; + tableRef._jsdo._copyRecord(tableRef, record, tableRef._data[index]); + if (deleteProdsProps) + tableRef._jsdo._deleteProdsProperties(tableRef._data[index], true); + } + delete tableRef._beforeImage[id]; + // End - Restore before image + }; + + this._undoDelete = function (tableRef, id, deleteProdsProps) { + /* Default to false */ + if (typeof(deleteProdsProps) == 'undefined') { + deleteProdsProps = false; + } + + // Restore from before image + var record = tableRef._beforeImage[id]; + + // Before image points to an existing record + if (record) { + var index = record._index; + delete record._index; + if (deleteProdsProps) + tableRef._jsdo._deleteProdsProperties(record, true); + + if ((index !== undefined) && (tableRef._data[index] === null)) { + tableRef._data[index] = record; + } + else { + tableRef._data.push(record); + index = tableRef._data.length - 1; + } + tableRef._index[id] = new progress.data.JSIndexEntry(index); + } + delete tableRef._beforeImage[id]; + // End - Restore before image + }; + + this._deleteComplete = function (jsdo, success, request) { + var xhr = request.xhr; + var jsrecord = request.jsrecord; + + try { + // Before firing trigger, delete prods properties so they don't appear in data + jsdo._deleteProdsProperties(jsrecord.data, false); + + jsrecord._tableRef.trigger("afterDelete", jsdo, jsrecord, request.success, request); + jsdo.trigger("afterDelete", jsdo, jsrecord, request.success, request); + + } finally { + request.complete = true; + jsdo._checkSaveComplete(xhr); + } + }; + + this._deleteSuccess = function (jsdo, success, request) { + var xhr = request.xhr; + var jsonObject = request.response; + var beforeJsonObject = null; + var dataSetJsonObject = null; + var data; + + //Even though this is _deleteSuccess, if before-image data is returned, the call of + // delete operation could return a success, but we have to check if error was returned + // in before-image data + var hasError = false; + if (jsdo._useBeforeImage("delete")) { + dataSetJsonObject = jsonObject[jsdo._dataSetName]; + beforeJsonObject = dataSetJsonObject["prods:before"]; + + if (beforeJsonObject) { + data = beforeJsonObject[request.jsrecord._tableRef._name]; + } + } + else { + data = jsdo._arrayFromDataObject(jsonObject, request.jsrecord._tableRef); + } + + if (data instanceof Array) { + if (data.length > 1) { + request.success = false; + throw new Error(msg.getMsgText("jsdoMSG100")); + } + } + + if (beforeJsonObject) { + hasError = jsdo._checkForDeleteError(dataSetJsonObject, xhr); + } + + if (hasError) + request.success = false; + + if (jsdo.autoApplyChanges) { + if (!hasError) { + // Clear before image + delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; + // End - Clear before image + } + else { + jsdo._deleteError(jsdo, success, request); + } + } + }; + + this._deleteError = function (jsdo, success, request) { + if (jsdo.autoApplyChanges) { + jsdo._undoDelete(request.jsrecord._tableRef, request.jsrecord.data._id); + } + }; + + this._createComplete = function (jsdo, success, request) { + var xhr = request.xhr; + var jsrecord = request.jsrecord; + + try { + // Before firing trigger, delete prods properties so they don't appear in data + jsdo._deleteProdsProperties(jsrecord.data, false); + + jsrecord._tableRef.trigger("afterCreate", jsdo, jsrecord, request.success, request); + jsdo.trigger("afterCreate", jsdo, jsrecord, request.success, request); + } finally { + request.complete = true; + jsdo._checkSaveComplete(xhr); + } + }; + + this._createSuccess = function (jsdo, success, request) { + var xhr = request.xhr; + var record = request.response; + var hasError = jsdo._mergeUpdateForCUD(record, xhr); + + if (hasError) + request.success = false; + + if (jsdo.autoApplyChanges) { + if (!hasError) { + // Clear before image + delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; + // End - Clear before image + } + else { + jsdo._createError(jsdo, success, request); + } + } + }; + + this._createError = function (jsdo, success, request) { + if (jsdo.autoApplyChanges) { + jsdo._undoCreate(request.jsrecord._tableRef, request.jsrecord.data._id); + } + }; + + + this._updateComplete = function (jsdo, success, request) { + var xhr = request.xhr; + var jsrecord = request.jsrecord; + try { + // Before firing trigger, delete prods properties so they don't appear in data + jsdo._deleteProdsProperties(jsrecord.data, false); + + jsrecord._tableRef.trigger("afterUpdate", jsdo, jsrecord, request.success, request); + jsdo.trigger("afterUpdate", jsdo, jsrecord, request.success, request); + } finally { + request.complete = true; + jsdo._checkSaveComplete(xhr); + } + }; + + this._updateSuccess = function (jsdo, success, request) { + var xhr = request.xhr; + var hasError = jsdo._mergeUpdateForCUD(request.response, xhr); + + if (hasError) { + request.success = false; + } + + if (jsdo.autoApplyChanges) { + if (!hasError) { + request.success = true; + // Clear before image + delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; + // End - Clear before image + } + else { + jsdo._updateError(jsdo, success, request); + } + } + }; + + this._updateError = function (jsdo, success, request) { + + if (jsdo.autoApplyChanges) { + request.success = false; + jsdo._undoUpdate(request.jsrecord._tableRef, request.jsrecord.data._id); + } + }; + + + this._saveChangesSuccess = function (jsdo, success, request) { + var records = request.response; + jsdo._mergeUpdateForSubmit(records, request.xhr); + + // Ensure that that the _lastErrors variable has been cleared + jsdo._clearErrors(); + var changes = jsdo.getChanges(); + jsdo._updateLastErrors(jsdo, null, changes); + + jsdo._setAllRecordsRejected(changes); + + if (jsdo.autoApplyChanges) { + jsdo._applyChanges(); + } + }; + + + this._saveChangesError = function (jsdo, success, request) { + jsdo._setAllRecordsRejected(true); + if (jsdo.autoApplyChanges) { + jsdo.rejectChanges(); + } + jsdo._updateLastErrors(jsdo, null, null, request); + }; + + /* _saveChangesSuccessTT + internal function called after a Submit of a temp-table (not DataSet) returns success + This method does not attempt to do any merging of records into the JSDO memory. The + absence of _id for the records means that the only way we could possibly do a "merge" + would be to delete the changed rceords in the JSDO memory and then add the records + that were returned form the data service, but that would invalidate the _id's that + the Kendo datasource depends on. The application programmmer must do the merging in + the afterSaveChanges handler + + *** Submit(temp-table) is not supported. This method will be removed in a future version. *** + */ + this._saveChangesSuccessTT = function (jsdo, success, request) { + var changes; + + // Ensure that that the _lastErrors variable has been cleared + jsdo._clearErrors(); + changes = jsdo.getChanges(); + jsdo._updateLastErrors(jsdo, null, changes); + jsdo._setAllRecordsRejected(false); + }; + + this._saveChangesComplete = function (jsdo, success, request) { + // Success with errors + if ((request.xhr.status >= 200 && request.xhr.status < 300) + && (jsdo._lastErrors.length > 0 || jsdo._someRecordsRejected)) { + request.success = false; + } + + // If saveChanges(true) was called, then we must fire create, update and delete triggers + // for each record that was sent to submit operation + if (jsdo._useSubmit === true) { + jsdo._fireCUDTriggersForSubmit(request); + } + + jsdo._undefWorkingRecord(); + jsdo._fireAfterSaveChanges(request.success, request); + + }; + + this._fireAfterSaveChanges = function (success, request) { + this.trigger("afterSaveChanges", this, success, request); + + if (request.jsrecords) { + if (request.deferred) { + if (success) { + request.deferred.resolve(this, success, request); + } + else { + request.deferred.reject(this, success, request); + } + } + } + else if (request.batch && request.batch.deferred) { + if (success) { + request.batch.deferred.resolve(this, success, request); + } + else { + request.batch.deferred.reject(this, success, request); + } + } + + // Clear error string when autoApplyChanges is true + var clearErrorString = this.autoApplyChanges; + + // This will be set if submit operation was performed + if (request.jsrecords) { + for (var idx = 0; idx < request.jsrecords.length; idx++) { + var jsrecord = request.jsrecords[idx]; + if (clearErrorString) { + delete jsrecord.data._errorString; + } + delete jsrecord.data["prods:rowState"]; + } + } + else if (request.batch && request.batch.operations) { + for (var idx = 0; idx < request.batch.operations.length; idx++) { + var jsrecord = request.batch.operations[idx].jsrecord; + if (clearErrorString) { + delete jsrecord.data._errorString; + } + } + } + }; + + /* + * Returns errors in response associated with the HTTP request.records related to the specified jsrecord. + */ + this._getErrorsFromRequest = function(request) { + var errors = [], // Array of objects with properties: type, id, error, errorNum, responseText + errorArray = [], + errorObject, + retValString, + j, + i; + + if (request && !request.success) { + if (request.xhr.status >= 400 && request.xhr.status < 600) { + try { + responseObject = JSON.parse(request.xhr.responseText); + + // responseText could be an array, an object or just text. + // If it is an array, each object would have properties _errors and optional _retVal. + // If it is not an array, the object would have properties _errors and optional _retVal. + // If it is text, the content could also be an HTML page, this error is handle using "HTTP Status". + if (responseObject instanceof Array) { + errorArray = responseObject; + } else if (responseObject instanceof Object) { + errorArray.push(responseObject); + } + for (i = 0; i < errorArray.length; i += 1) { + errorObject = errorArray[i]; + if (errorObject._retVal) { + errors.push({ + type: progress.data.JSDO.RETVAL, + error: errorObject._retVal + }); + retValString = errorObject._retVal; + } else { + retValString = null; + } + if (errorObject._errors instanceof Array) { + for (j = 0; j < errorObject._errors.length; j += 1) { + if ((errorObject._errors[j]._errorNum === 0) + && (errorObject._errors[j]._errorMsg === retValString)) { + // Suppress additional error msg if it is same as return value + continue; + } + errors.push({ + type: progress.data.JSDO.APP_ERROR, + error: errorObject._errors[j]._errorMsg, + errorNum: errorObject._errors[j]._errorNum + }); + } + } + } + } + catch (e) { + // Ignore exceptions + } + } + if (request.exception) { + errors.push({ + type: progress.data.JSDO.ERROR, + error: request.exception + }); + } + if (errors.length === 0 + && request.xhr + && (request.xhr.status >= 400 && request.xhr.status < 600)) { + errors.push({ + type: progress.data.JSDO.ERROR, + error: "Error: HTTP Status " + request.xhr.status + " " + request.xhr.statusText, + responseText: request.xhr.responseText + }); + } + } + return errors; + }; + + this._updateLastErrors = function (jsdo, batch, changes, request) { + var errors, + errorText, + responseObject, + i, + j, + buf; + + if (batch) { + if (batch.operations === undefined) return; + for (i = 0; i < batch.operations.length; i++) { + request = batch.operations[i]; + if (!request.success && request.xhr) { + if (request.xhr.status >= 200 && request.xhr.status < 300) { + // Add error string to jsdo._lastErrors + jsdo._lastErrors.push({errorString: request.jsrecord.data._errorString}); + // Add error object to jsdo.._lastErrors + jsdo._buffers[request.jsrecord._tableRef._name]._lastErrors.push({ + type: progress.data.JSDO.DATA_ERROR, + id: request.jsrecord.data._id, + error: request.jsrecord.data._errorString}); + } + else { + errors = this._getErrorsFromRequest(request); + errorText = ""; + for (j = 0; j < errors.length; j += 1) { + if (errors.length > 1 && errors[j].error.indexOf("(7243)") != -1) { + // If there are more error messages + // supress error "The Server application has returned an error. (7243)" + continue; + } + // Add error to table reference + if (request.jsrecord + && (errors[j].type === progress.data.JSDO.APP_ERROR + || errors[j].type === progress.data.JSDO.RETVAL)) { + errors[j].id = request.jsrecord.data._id; + request.jsrecord._tableRef._lastErrors.push(errors[j]); + } + if (errorText.length === 0) { + errorText = errors[j].error; + } + else { + errorText += "\n" + errors[j].error; + } + } + // Add error string to jsdo._lastErrors + jsdo._lastErrors.push({errorString: errorText}); + } + } + } + } + else if (changes instanceof Array) { + for (i = 0; i < changes.length; i++) { + if (changes[i].record && changes[i].record.data._errorString !== undefined) { + jsdo._lastErrors.push({errorString: changes[i].record.data._errorString}); + jsdo._buffers[changes[i].record._tableRef._name]._lastErrors.push({ + type: progress.data.JSDO.DATA_ERROR, + id: changes[i].record.data._id, + error: changes[i].record.data._errorString}); + } + } + } + else if (request + && !request.success + && request.xhr + && ((request.xhr.status >= 400 && request.xhr.status < 600) || request.xhr.status === 0)) { + errors = this._getErrorsFromRequest(request); + errorText = ""; + for (j = 0; j < errors.length; j += 1) { + if (errors.length > 1 && errors[j].error.indexOf("(7243)") != -1) { + // If there are more error messages + // supress error "The Server application has returned an error. (7243)" + continue; + } + // Add error to all table references + for (buf in this._buffers) { + this._buffers[buf]._lastErrors.push(errors[j]); + } + if (errorText.length === 0) { + errorText = errors[j].error; + } + else { + errorText += "\n" + errors[j].error; + } + } + jsdo._lastErrors.push({errorString: errorText}); + } + }; + + // Check if all the xhr operations associated with the batch for which + // this xhr object is related have completed (not necessarily to success). + // If all XHR operations have completed this fires 'afterSaveChanges' event + this._checkSaveComplete = function (xhr) { + if (xhr.request) { + var jsdo = xhr.request.jsdo; + var batch = xhr.request.batch; + // OE00229270 Should only do afterSaveChanges if _async + if (jsdo && batch && jsdo._async) { + if (jsdo._isBatchComplete(batch)) { + var success = jsdo._isBatchSuccess(batch); + var request = { + batch: batch, + success: success + }; + jsdo._undefWorkingRecord(); + + // Save error messages + jsdo._lastErrors = []; + if (!success && batch.operations) { + jsdo._updateLastErrors(jsdo, batch, null); + } + this._setAllRecordsRejected(batch); + + jsdo._fireAfterSaveChanges(success, request); + } + } + } + }; + + + /* + * determine if a batch of XHR requests has completed in which all requests are successful + */ + this._isBatchSuccess = function (batch) { + if (batch.operations) { + for (var i = 0; i < batch.operations.length; i++) { + if (!batch.operations[i].success) { + return false; + } + } + } + return true; + }; + + /* + * determine if all XHR requests from the batch of saves have completed (not necessarily to success) + */ + this._isBatchComplete = function (batch) { + if (batch.operations) { + for (var i = 0; i < batch.operations.length; i++) { + var request = batch.operations[i]; + // we have to check against the 'complete' flag because xhr.readyState + // might be set async by the browser + // while we're still in the middle of processing some other requests's response + if (!request.complete) { + return false; + } + } + } + return true; + }; + + this._mergeInvoke = function (jsonObject, xhr) { + var operation; + if (xhr.request.fnName !== undefined + && xhr.jsdo._resource.fn[xhr.request.fnName] !== undefined) { + operation = xhr.jsdo._resource.fn[xhr.request.fnName].operation; + } + else + operation = null; + if (operation === undefined) { + // Operation data is only required for invoke operations with mergeMode: true + operation = null; + for (var i = 0; i < xhr.jsdo._resource.operations.length; i++) { + if (xhr.jsdo._resource.operations[i].name == xhr.request.fnName) { + operation = xhr.jsdo._resource.operations[i]; + break; + } + } + xhr.jsdo._resource.fn[xhr.request.fnName].operation = operation; + } + if (operation !== null && operation.mergeMode) { + try { + var mergeMode = progress.data.JSDO["MODE_" + operation.mergeMode.toUpperCase()]; + if (mergeMode === null) { + throw new Error(msg.getMsgText("jsdoMSG030", "mergeMode property", + "EMPTY, APPEND, MERGE or REPLACE")); + } + if (xhr.jsdo._resource.idProperty === undefined) { + throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, + " by mergeMode property in invoke operation")); + } + var dataParameterName; + if (xhr.jsdo.isDataSet()) { + dataParameterName = xhr.jsdo._resource._dataSetName; + } + else if (xhr.jsdo._resource.dataProperty !== undefined) { + dataParameterName = xhr.jsdo._resource.dataProperty; + } + else if (xhr.jsdo._resource._tempTableName !== undefined) { + dataParameterName = xhr.jsdo._resource._tempTableName; + } + else { + throw new Error(msg.getMsgText("jsdoMSG111", "")); + } + + var found = false; + for (var i = 0; i < operation.params.length; i++) { + if (operation.params[i].name == dataParameterName) { + if (operation.params[i].type.indexOf('RESPONSE_BODY') != -1) { + if ((operation.params[i].xType !== undefined) + && (operation.params[i].xType != 'DATASET') + && (operation.params[i].xType != 'TABLE') + && (operation.params[i].xType != 'ARRAY')) { + throw new Error(msg.getMsgText("jsdoMSG113", operation.params[i].xType, + dataParameterName, xhr.request.fnName)); + } + found = true; + break; + } + } + } + + if (!found) { + throw new Error(msg.getMsgText("jsdoMSG112", dataParameterName, xhr.request.fnName)); + } + xhr.jsdo.addRecords(xhr.request.response[dataParameterName], + mergeMode, [xhr.jsdo._resource.idProperty], false, true); + } + catch (e) { + xhr.request.success = false; + xhr.request.exception = e; + } + } + }; + + this.onReadyStateChangeGeneric = function () { + var xhr = this; + if (xhr.readyState == 4) { + var request = xhr.request; + + /* try to parse response even if request is considered "failed" due to http status */ + try { + request.response = JSON.parse(xhr.responseText); + // in some cases the object back from appserver has a "response" property which represents + // the real content of the JSON...happens when multiple output parameters are returned. + // this of course assumes no one names their root object "response". + if (request.response && request.response.response) { + request.response = request.response.response; + } + } catch (e) { + request.response = undefined; + } + + try { + if ((xhr.status >= 200 && xhr.status < 300) + || (xhr.status === 0 && xhr.responseText !== "")) { + + request.success = true; + // get the Client Context ID (AppServer ID) + xhr.jsdo._session._saveClientContextId(xhr); + if ((typeof xhr.onSuccessFn) == 'function') { + var operation; + if (xhr.request.fnName !== undefined + && xhr.jsdo._resource.fn[xhr.request.fnName] !== undefined) { + operation = xhr.jsdo._resource.fn[xhr.request.fnName].operation; + } + else + operation = null; + if ((operation === undefined) || (operation !== null && operation.mergeMode)) + xhr.jsdo._mergeInvoke(request.response, xhr); + if (request.success) + xhr.onSuccessFn(xhr.jsdo, request.success, request); + else if ((typeof xhr.onErrorFn) == 'function') + xhr.onErrorFn(xhr.jsdo, request.success, request); + } + + } else { + request.success = false; + if (xhr.status === 0) { + request.exception = new Error(msg.getMsgText("jsdoMSG101")); + } + if ((typeof xhr.onErrorFn) == 'function') { + xhr.onErrorFn(xhr.jsdo, request.success, request); + } + } + } catch (e) { + request.success = false; + request.exception = e; + if ((typeof xhr.onErrorFn) == 'function') { + xhr.onErrorFn(xhr.jsdo, request.success, request); + } + } + // get the Client Context ID (AppServer ID) + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + + if ((typeof xhr.onCompleteFn) == 'function') { + xhr.onCompleteFn(xhr.jsdo, request.success, request); + } + + } + }; + + /* + * Accepts changes for all table references in the JSDO. + */ + this.acceptChanges = function () { + for (var buf in this._buffers) { + this._buffers[this._buffers[buf]._name].acceptChanges(); + } + }; + + /* + * Rejects changes for the table references in the JSDO. + */ + this.rejectChanges = function () { + for (var buf in this._buffers) { + this._buffers[this._buffers[buf]._name].rejectChanges(); + } + }; + + /* + * Returns an array with changes for all table references in the JSDO. + */ + this.getChanges = function () { + var result = []; + for (var buf in this._buffers) { + var changes = this._buffers[this._buffers[buf]._name].getChanges(); + result = result.concat(changes); + } + return result; + }; + + this.hasChanges = function () { + for (var buf in this._buffers) { + if (this._buffers[this._buffers[buf]._name].hasChanges()) + return true; + } + return false; + }; + + /* + * Private method to apply changes for all table references in the JSDO. + * If _errorString has been set for a row, rejectRowChanges() is called. + * If it has not been set, acceptRowChanges() is called. + */ + this._applyChanges = function () { + for (var buf in this._buffers) { + this._buffers[this._buffers[buf]._name]._applyChanges(); + } + }; + + /* + * Accepts row changes for the working record using the JSDO reference. + */ + this.acceptRowChanges = function () { + if (this._defaultTableRef) + return this._defaultTableRef.acceptRowChanges(); + throw new Error(msg.getMsgText("jsdoMSG001", "acceptRowChanges()")); + }; + + /* + * Reject row changes for the working record using the JSDO reference. + */ + this.rejectRowChanges = function () { + if (this._defaultTableRef) + return this._defaultTableRef.rejectRowChanges(); + throw new Error(msg.getMsgText("jsdoMSG001", "rejectRowChanges()")); + }; + + /* + * Sets complete set of properties for the jsdo. All existing properties are replaced with new set + */ + this.setProperties = function( propertiesObject ) { + var prop; + + if (arguments.length < 1) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'setProperties', 1)); + } + if (arguments.length > 1) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'setProperties', 1)); + } + if ( typeof propertiesObject == "object" ) { + /* Copy properties of the propertiesObject argument into _properties. + * Note that if object passed in has a prototype, this code copies them too) + */ + this._properties = {}; + + for (prop in propertiesObject) { + if( propertiesObject.hasOwnProperty(prop) ) { + if (typeof propertiesObject[prop] !== "function" ) { + this._properties[prop] = propertiesObject[prop]; + } + } + } + } + else if ( (propertiesObject === undefined) || (propertiesObject === null) ) { + this._properties = {}; + } + else { + // {1}: Parameter {1} must be of type {3} in {4} call. + throw new Error(progress.data._getMsgText("jsdoMSG121", 'JSDO', 1, 'Object', + 'setProperties')); + } + }; + + /* + * Set or remove an individual property in the property set maintained by the jsdo. + * This operates only on the property identified by propertyName; + * all other existing properties remain as they are. + * If the propertyName is not part of the context, this call adds it. + * If it exists, it is updated, unless - + * If propertyValue is undefined, this call removes the property + */ + this.setProperty = function( propertyName, propertyValue) { + if (arguments.length < 2) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', + 'setProperty', 2)); + } + if (arguments.length !== 2) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", "JSDO", + "setProperty", 2)); + } + if (typeof propertyName !== "string") { + // {1}: Parameter {1} must be of type {3} in {4} call. + throw new Error(progress.data._getMsgText("jsdoMSG121", 'JSDO', 1, 'string', + 'setProperty')); + } + + if ( propertyValue === undefined ) { + delete this._properties[propertyName]; // OK if it doesn't exist -- no error + } + else { + this._properties[propertyName] = propertyValue; + } + }; + + /* + * Gets the set of jsdo properties. Returns an object containing all the properties + */ + this.getProperties = function( ) { + if (arguments.length > 0) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperties', 0)); + } + return this._properties; + }; + + /* Gets the value of an individual property in the jsdo property set + */ + this.getProperty = function( propertyName) { + if (arguments.length < 1) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperty', 1)); + } + if (arguments.length > 1) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperty', 1)); + } + return this._properties[propertyName]; + + }; + + /////////////////////////////////////////////////////////////////////////// + // + // The following methods provide support for Object Pesistence + + /* + * Saves JSDO memory (and optionally pending changes) to local storage. + * + * saveLocal() + * saveLocal(name) + * saveLocal(dataMode) + * saveLocal(name, dataMode) + * + */ + this.saveLocal = function saveLocal(arg1, arg2) { + var name; + var dataMode; + + if (arguments.length > 2) { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); + } + + if (typeof(arg1) == 'string' || arg1 === null || arg1 === undefined) { + name = arg1; + dataMode = arg2; + } + else { + name = null; + dataMode = arg1; + } + + if (name === undefined || name === null || name === "") { + name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; + } + if (typeof(dataMode) == 'undefined') { + dataMode = progress.data.JSDO.ALL_DATA; + } + else { + switch (dataMode) { + case progress.data.JSDO.ALL_DATA: + case progress.data.JSDO.CHANGES_ONLY: + break; + default: + throw new Error(msg.getMsgText("jsdoMSG115", arguments.callee.name)); + } + } + + if (this._localStorage === null) { + // Must first instantiate _localStorage object + this._localStorage = new progress.data.LocalStorage(); + } + + var dataObj = this._prepareDataObjectForLocalStorage(dataMode); + this._localStorage.saveToLocalStorage(name, dataObj); + }; + + /* + * Reads localStorage (based upon name) into JSDO memory + * (localStorage may or may not have pending changes). + * readLocal() + * readLocal(name) + * + */ + this.readLocal = function readLocal(name) { + if (arguments.length > 1) { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); + } + if (name === undefined || name === null || name === "") { + name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; + } + else if (typeof(name) != 'string') { + throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); + } + + if (this._localStorage === null) { + this._localStorage = new progress.data.LocalStorage(); + } + + var object = this._localStorage.readFromLocalStorage(name); + + // If storage area does not exist (i.e. object = null) then don't update JSDO local memory + if (object) { + if (this._hasMatchingSchema(object) === false) + throw new Error(msg.getMsgText("jsdoMSG117", name)); + + // For readLocal(), JSDO should first be emptied of data, so using MODE_EMPTY + this._restoreFromLocalStorage(object, progress.data.JSDO.MODE_EMPTY); + } + + return object !== null; + }; + + /* + * Reads localStorage (based upon name) into JSDO memory + * (localStorage may or may not have pending changes). + * addLocalRecords(addMode) + * addLocalRecords(addMode, keyFields) + * addLocalRecords(name, addMode) + * addLocalRecords(name, addMode, keyFields) + */ + this.addLocalRecords = function addLocalRecords(arg1, arg2, arg3) { + var name; + var addMode; + var keyFields; + + if (arguments.length < 1) { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); + } + + if (typeof(arg1) == 'string') { + name = arg1; + addMode = arg2; + keyFields = arg3; + } + else { + name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; + addMode = arg1; + keyFields = arg2; + } + + if (typeof(name) == 'undefined' || name === null || name === "") { + name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; + } + else if (typeof(name) != 'string') { + throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); + } + + if (addMode != progress.data.JSDO.MODE_REPLACE) { + throw new Error(msg.getMsgText("jsdoMSG115", arguments.callee.name)); + } + + if (this._localStorage === null) { + this._localStorage = new progress.data.LocalStorage(); + } + + var object = this._localStorage.readFromLocalStorage(name); + + // If storage area does not exist (i.e. object = null) then don't update JSDO local memory + if (object) { + if (this._hasMatchingSchema(object) === false) + throw new Error(msg.getMsgText("jsdoMSG117", name)); + + try { + this._restoreFromLocalStorage(object, addMode, keyFields); + } + catch (e) { + var text = e.message; + throw new Error(text.replace(new RegExp('addRecords', 'g'), 'addLocalRecords')); + } + } + + return object !== null; + }; + + + /* + * This method returns True if each buffer in the jsdo contains a primary key. + */ + this._containsPrimaryKeys = function _containsPrimaryKeys() { + + for (var buf in this._buffers) { + if (this._buffers[buf]._primaryKeys === null) + return false; + } + + return true; + }; + + /* + * Compares JSDO's dataset/table names with those in specified storage object. + * Returns true if they match (or if storageObject is null or empty), else false. + */ + this._hasMatchingSchema = function _hasMatchingSchema(storageObject) { + var isValid = true; + + if (storageObject === null || (Object.keys(storageObject).length === 0)) + return true; + + + if (this._dataSetName) { + if (storageObject[this._dataSetName]) { + for (var buf in this._buffers) + if (storageObject[this._dataSetName][buf] === undefined) { + isValid = false; + break; + } + } + else + isValid = false; // dataset should be in storage area + } + else if (this._dataProperty) { + // If array, we had to wrap in "fake" dataset, so unwrap it + storageObject = storageObject["_localStorage"]; + if (storageObject === undefined || storageObject[this._dataProperty] === undefined) + isValid = false; + } + else { + // If temp-table, we had to wrap in "fake" dataset, so unwrap it + storageObject = storageObject["_localStorage"]; + if (storageObject === undefined || storageObject[this._defaultTableRef._name] === undefined) + isValid = false; + } + + return isValid; + }; + + + /* + * Clears the data saved to local storage. + * + * deleteLocal() + * deleteLocal(name) + */ + this.deleteLocal = function deleteLocal(name) { + if (arguments.length > 1) { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); + } + if (name === undefined || name === null || name === "") { + name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; + } + else if (typeof(name) != 'string') { + throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); + } + + if (this._localStorage === null) { + this._localStorage = new progress.data.LocalStorage(); + } + this._localStorage.clearLocalStorage(name); + }; + + + // This method is used by saveLocal() to return a jsonObject with current JSDO data based upon option. + // + // In order to take advantage of existing code (createChangeSet() and addRecords()) and particularly + // to use the processing of before-data in addRecords(), for tables and arrays, we create a dummy + // dataset name: _localStorage. + this._prepareDataObjectForLocalStorage = function (option) { + + var storageObject = {}; + + // DataSets + if (this._dataSetName) { + switch (option) { + case progress.data.JSDO.ALL_DATA: + storageObject = this._createDataAndChangeSet(this._dataSetName); + break; + + case progress.data.JSDO.CHANGES_ONLY: + storageObject = this._createChangeSet(this._dataSetName, true); + break; + } + } + // Arrays + else if (this._dataProperty) { + switch (option) { + case progress.data.JSDO.ALL_DATA: + storageObject = this._createDataAndChangeSet("_localStorage"); + break; + + case progress.data.JSDO.CHANGES_ONLY: + storageObject = this._createChangeSet("_localStorage", true); + break; + } + } + // Temp Tables + else { + switch (option) { + case progress.data.JSDO.ALL_DATA: + storageObject = this._createDataAndChangeSet("_localStorage"); + break; + + case progress.data.JSDO.CHANGES_ONLY: + storageObject = this._createChangeSet("_localStorage", true); + break; + } + } + + return storageObject; + }; + + + // Restore the data retrieved from local storage to the JSDO based upon the specified addMode + this._restoreFromLocalStorage = function (storageObject, addMode, keyFields) { + + if (storageObject && (Object.keys(storageObject).length > 0)) { + if (this._dataSetName) { + // Walk thru all tables to retrieve data + for (var buf in this._buffers) + this._restoreDataForTable(this._buffers[buf], storageObject, addMode, keyFields); + } + // Either temp-table or array + else + this._restoreDataForTable(this._defaultTableRef, storageObject, addMode, keyFields); + } + else if (addMode === progress.data.JSDO.MODE_EMPTY) + this._clearData(); + }; + + + this._restoreDataForTable = function (tableRef, jsonObject, addMode, keyFields) { + + // If primaryKeys not found, check if the idProperty is there + keyFields = keyFields !== undefined ? keyFields : tableRef._primaryKeys; + if (keyFields === undefined && this._resource.idProperty) { + keyFields = []; + keyFields[0] = this._resource.idProperty; + } + + if (this._dataSetName) { + var oldUseRelationships = this.useRelationships; + // Turn off useRelationships since addRecords() does not use the working record + this.useRelationships = false; + + try { + tableRef.addRecords(jsonObject, addMode, keyFields); + } finally { + // Restore useRelationships + this.useRelationships = oldUseRelationships; + } + } + // else it's either an array (this._dataProperty) or a temp-table + else { + // Creating dummy dataset name: "_localStorage" for tables and arrays + this._dataSetName = "_localStorage"; + tableRef.addRecords(jsonObject, addMode, keyFields); + this._dataSetName = null; + } + }; + + this.getMethodProperties = function(operation, name) { + var idx; + + if (this._resource._operations) { + if (this._resource._operations[operation]) { + return this._resource._operations[operation]; + } + } + else { + this._resource._operations = {}; + } + for (var idx = 0; idx < this._resource.operations.length; idx++) { + if (this._resource.operations[idx].type == operation) { + return (this._resource._operations[operation] = this._resource.operations[idx]); + } + } + }; + + /////////////////////////////////////////////////////////////////////////// + + // Load data + if (autoFill) + this.fill(); + + }; // End of JSDO + + // Constants for progress.data.JSDO + if ((typeof Object.defineProperty) == 'function') { + Object.defineProperty(progress.data.JSDO, 'MODE_APPEND', { + value: 1, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'MODE_EMPTY', { + value: 2, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'MODE_MERGE', { + value: 3, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'MODE_REPLACE', { + value: 4, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'ERROR', { + value: -1, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'APP_ERROR', { + value: -2, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'RETVAL', { + value: -3, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'DATA_ERROR', { + value: -4, + enumerable: true + }); + } else { + progress.data.JSDO.MODE_APPEND = 1; + progress.data.JSDO.MODE_EMPTY = 2; + progress.data.JSDO.MODE_MERGE = 3; + progress.data.JSDO.MODE_REPLACE = 4; + } + + /* CRUD */ + progress.data.JSDO._OP_CREATE = 1; + progress.data.JSDO._OP_READ = 2; + progress.data.JSDO._OP_UPDATE = 3; + progress.data.JSDO._OP_DELETE = 4; + progress.data.JSDO._OP_SUBMIT = 5; + + /* Offline support: saving data to local storage */ + progress.data.JSDO.ALL_DATA = 1; + progress.data.JSDO.CHANGES_ONLY = 2; + + // Arrays elements as individual fields + // Separator must have at least one characters + progress.data.JSDO.ARRAY_INDEX_SEPARATOR = "_"; + +// setup inheritance for JSDO + progress.data.JSDO.prototype = new progress.util.Observable(); + progress.data.JSDO.prototype.constructor = progress.data.JSDO; + progress.data.JSDO.prototype.toString = function (radix) { + return "JSDO"; + }; + +// setup inheritance for table reference + progress.data.JSTableRef.prototype = new progress.util.Observable(); + progress.data.JSTableRef.prototype.constructor = progress.data.JSTableRef; + progress.data.JSTableRef.prototype.toString = function (radix) { + return "JSTableRef"; + }; + + // Built-in Plugins + progress.data.PluginManager.addPlugin("JFP", { + requestMapping: function(jsdo, params, info) { + var sortFields, + field, + fieldName, + fieldInfo, + tableName, + filter, + sortDir, + ablFilter, + sqlQuery, + methodProperties, + capabilities, + index, + position, + option, + capabilitiesObject, + reqCapabilities = { + filter: { options: [ "ablFilter", "sqlQuery" ], mapping: undefined }, + top: { options: [ "top" ], mapping: undefined }, + skip: { options: [ "skip" ], mapping: undefined }, + id: { options: [ "id" ], mapping: undefined }, + sort: { options: [ "orderBy" ], mapping: undefined } + }, + doConversion = true, + param; + + if (info.operation === "read") { + capabilitiesObject = {}; + methodProperties = jsdo.getMethodProperties(info.operation); + capabilities = methodProperties.capabilities; + + if (capabilities) { + capabilities = capabilities.replace(/\s/g, "").split(","); + for (index = 0; index < capabilities.length; index += 1) { + capabilitiesObject[capabilities[index]] = true; + } + } + for (param in params) { + if (param && (params[param] !== undefined) && reqCapabilities[param]) { + for (index = 0; index < reqCapabilities[param].options.length; index += 1) { + option = reqCapabilities[param].options[index]; + if (capabilitiesObject[option]) { + reqCapabilities[param].mapping = option; + break; + } + } + if (!reqCapabilities[param].mapping) { + throw new Error(msg.getMsgText("jsdoMSG120", + reqCapabilities[param].options.join("' or '"), param)); + } + } + } + + if (jsdo._defaultTableRef && params.tableRef === undefined) { + tableName = jsdo._defaultTableRef._name; + } + else { + tableName = params.tableRef; + } + + if (params.sort) { + // Convert sort expression to JFP format + + if (typeof(params.sort) === "object" && !(params.sort instanceof Array)) { + // Kendo UI sort format - object + // Make params.sort an array + params.sort = [params.sort]; + } + sortFields = ""; + for (index = 0; index < params.sort.length; index += 1) { + field = params.sort[index]; + sortDir = ""; + + if (typeof(field) === "string") { + // setSortFields format + // Extract fieldName and sortDir from string + fieldName = field; + position = field.indexOf(":"); + if (position !== -1) { + sortDir = fieldName.substring(position + 1); + fieldName = fieldName.substring(0, position); + switch(sortDir.toLowerCase()) { + case "desc": + case "descending": + sortDir = "desc"; + break; + } + } + } else { + // Kendo UI sort format - array + // Extract fieldName and sortDir from object + fieldName = field.field; + if (params.sort[index].dir === "desc") { + sortDir = params.sort[index].dir; + } + } + if (tableName) { + // Use original fieldName instead of serialized name + fieldInfo = jsdo[tableName]._fields[fieldName.toLowerCase()]; + if (fieldInfo && fieldInfo.origName) { + fieldName = fieldInfo.origName; + } + } + if (sortDir === "desc") { + fieldName += " DESC"; + } + sortFields += fieldName; + if (index < params.sort.length - 1) { + sortFields += ","; + } + } + } + + if (params.filter) { + // If filter is specified as string, then no conversion is necessary + if (typeof params.filter === 'string') { + doConversion = false; + } + + params.tableRef = tableName; + + if (doConversion && (params.tableRef === undefined)) { + throw new Error(msg.getMsgText("jsdoMSG045", "fill() or read()", "params", + "tableRef")); + } + + if (reqCapabilities["filter"].mapping === "ablFilter") { + if (doConversion) { + ablFilter = progress.util._convertToABLWhereString( + jsdo._buffers[params.tableRef], params.filter); + } + else { + ablFilter = params.filter; + } + } + else if (reqCapabilities["filter"].mapping === "sqlQuery") { + if (doConversion) { + sqlQuery = progress.util._convertToSQLQueryString( + jsdo._buffers[params.tableRef], params.filter, true); + } + else { + sqlQuery = params.filter; + } + } + } + + filter = JSON.stringify({ + ablFilter: ablFilter, + sqlQuery: sqlQuery, + orderBy: sortFields, + skip: params.skip, + top: params.top + }); + + params = {filter: filter}; + } + return params; + } + }); + + if (typeof progress.ui == 'undefined') + progress.ui = {}; + progress.ui.UITableRef = function UITableRef(tableRef) { + this._tableRef = tableRef; + this._listview = null; + this._detailPage = null; + this._listviewContent = undefined; + + this.addItem = function (format) { + var detailForm; + + if (!this._tableRef.record) + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + + if (!this._listview) return; + + format = format ? format : this._listview.format; + detailForm = (this._detailPage && this._detailPage.name) ? this._detailPage.name : ""; + + if (this._listviewContent === undefined) { + this.clearItems(); + } + var text = this._listview.itemTemplate ? + this._listview.itemTemplate : progress.ui.UIHelper._itemTemplate; + + text = text.replace(new RegExp('{__format__}', 'g'), format); + text = text.replace(new RegExp('{__id__}', 'g'), this._tableRef.record.data._id); + text = text.replace(new RegExp('{__page__}', 'g'), detailForm); + + for (var field in this._tableRef.record.data) { + var value = this._tableRef.record.data[field]; + text = text.replace(new RegExp('{' + field + '}', 'g'), + (value !== undefined && value !== null) ? value : ""); + } + + this._listviewContent += text; + }; + + this.clearItems = function () { + if (this._listview) { + this._listviewContent = ''; + var listviewElement = document.getElementById(this._listview.name); + if (listviewElement) { + listviewElement.innerHTML = ''; + } + } + }; + + this._getFormFieldValue = function (fieldName, detailPageName) { + var value = null; + + if (detailPageName === undefined) { + if (this._detailPage && this._detailPage.name) + detailPageName = this._detailPage.name; + } + + if (typeof($) == 'function' && detailPageName) { + field = $("#" + detailPageName + " #" + fieldName); + if (!field || field.length === 0) + field = $("#" + detailPageName + ' [dsid="' + fieldName + '"]'); + if (field && field.length == 1) + value = field.val(); + } + else { + field = document.getElementById(fieldName); + if (field) { + value = field.value; + } + } + + return value; + }; + + this._setFormField = function (fieldName, value, detailPageName) { + var field = null; + + if (detailPageName === undefined) { + if (this._detailPage && this._detailPage.name) + detailPageName = this._detailPage.name; + } + + if (typeof($) == 'function' && detailPageName) { + field = $("#" + detailPageName + " #" + fieldName); + if (!field || field.length === 0) + field = $("#" + detailPageName + ' [dsid="' + fieldName + '"]'); + if (field && field.length == 1) + field.val(value); + } + else { + field = document.getElementById(fieldName); + if (field) { + field.value = value; + } + } + }; + + /* + * Assigns field values from the form. + */ + this.assign = function (detailPageName) { + if (!this._tableRef.record) + throw new Error(msg.getMsgText("jsdoMSG002", this._tableRef._name)); + if ((arguments.length !== 0) && (typeof detailPageName != 'string')) + throw new Error(msg.getMsgText("jsdoMSG024", "UIHelper", "assign()")); + + // Ensure creation of before image record + this._tableRef.record.assign(null); + + var fieldName; + var schema = this._tableRef.getSchema(); + for (var i = 0; i < schema.length; i++) { + fieldName = schema[i].name; + if (fieldName == '_id') continue; + var value = this._getFormFieldValue(fieldName, detailPageName); + // CR OE00241289 Should always copy over field value unless undefined, + // user may have explicitly set it to blank + if (typeof value != 'undefined') { + if (typeof value == 'string' && schema[i].type != 'string') { + value = this._tableRef._jsdo._convertType(value, + schema[i].type, + schema[i].items ? schema[i].items.type : null); + } + this._tableRef.record.data[fieldName] = value; + } + } + + // Ensure order of record + this._tableRef.record._sortRecord(); + + return true; + }; + + this.display = function (pageName) { + if (!this._tableRef.record) + throw new Error(msg.getMsgText("jsdoMSG002", this._tableRef._name)); + + // Display record to form + var schema = this._tableRef.getSchema(); + for (var i = 0; i < schema.length; i++) { + this._setFormField(schema[i].name, this._tableRef.record.data[schema[i].name], pageName); + } + this._setFormField('_id', this._tableRef.record.data._id, pageName); + }; + + this.showListView = function () { + if (!this._listview) return; + + var uiTableRef = this; + var listviewElement; + if (typeof($) == 'function') { + listviewElement = $("#" + this._listview.name); + if (listviewElement && listviewElement.length == 1) { + listviewElement.html(this._listviewContent ? this._listviewContent : ''); + try { + if (listviewElement.attr("data-filter") === "true" + && typeof listviewElement.filterable === "function") { + listviewElement.filterable("refresh"); + } + else { + listviewElement.listview("refresh"); + } + } + catch (e) { + // Workaround for issue with JQuery Mobile throwning exception on refresh + } + } + + if (this._listview.autoLink) { + // Add trigger for 'tap' event to items + $("#" + this._listview.name + " li").each( + function (/* index */) { + $(this).bind('click', + function (/* event, ui */) { + var jsrecord = uiTableRef.getListViewRecord(this); + uiTableRef.display(); + if (typeof(uiTableRef._listview.onSelect) == 'function') { + uiTableRef._listview.onSelect(event, this, jsrecord); + } + }); + }); + } + } + else { + listviewElement = document.getElementById(this._listview.name); + if (listviewElement) { + listviewElement.innerHTML = this._listviewContent; + } + + if (this._listview.autoLink) { + var element = document.getElementById(this._listview.name); + if (element && element.childElementCount > 0) { + for (var i = 0; i < element.children.length; i++) { + element.children[i].onclick = function () { + var jsrecord = uihelper.getListViewRecord(this); + uihelper.display(); + if (typeof(uiTableRef._listview.onSelect) == 'function') { + uiTableRef._listview.onSelect(event, this, jsrecord); + } + }; + } + } + } + } + + this._listviewContent = undefined; + }; + + this.getFormFields = function (fields) { + if (!this._tableRef._schema) + return ''; + if (!(fields instanceof Array)) + fields = null; + else { + var tmpFields = {}; + for (var i = 0; i < fields.length; i++) { + tmpFields[fields[i]] = fields[i]; + } + fields = tmpFields; + } + var htmltext; + if (!fields || fields['_id']) { + htmltext = ''; + } + else + htmltext = ''; + htmltext += '
'; + + for (var i = 0; i < this._tableRef._schema.length; i++) { + var fieldName = this._tableRef._schema[i].name; + if (fieldName == '_id') continue; + if (fieldName.length > 0 && fieldName.charAt(0) == '_') continue; + if (fields && fields[fieldName] === undefined) continue; + var fieldLabel = this._tableRef._schema[i].title ? + this._tableRef._schema[i].title : this._tableRef._schema[i].name; + var text = (this._detailPage && this._detailPage.fieldTemplate) ? + this._detailPage.fieldTemplate : progress.ui.UIHelper._fieldTemplate; + text = text.replace(new RegExp('{__label__}', 'g'), fieldLabel); + text = text.replace(new RegExp('{__name__}', 'g'), this._tableRef._schema[i].name); + htmltext += text; + } + htmltext += '
'; + fields = null; + return htmltext; + }; + + this.getListViewRecord = function (htmlIElement) { + var id = htmlIElement.getAttribute('data-id'); + return this._tableRef.findById(id); + }; + + this.getFormRecord = function (detailPageName) { + var id = this._getFormFieldValue('_id', detailPageName); + return this._tableRef.findById(id); + }; + + this._getIdOfElement = function (name) { + if (typeof($) == 'function') { + var element = $("#" + name); + if (!element || element.length === 0) { + element = $('[dsid="' + name + '"]'); + if (element && element.length == 1) { + var id = element.attr("id"); + if (id) + return id; + } + } + } + return name; + }; + + this.setDetailPage = function setDetailPage(obj) { + if (!obj || (typeof(obj) != 'object')) + throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "object")); + if (!obj.name || (typeof(obj.name) != 'string')) + throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "name")); + this._detailPage = obj; + this._detailPage.name = this._getIdOfElement(this._detailPage.name); + }; + this.setListView = function setListView(obj) { + if (!obj || (typeof(obj) != 'object')) + throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "object")); + if (!obj.name || (typeof(obj.name) != 'string')) + throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "name")); + if (obj.format && (typeof(obj.name) != 'string')) + throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "format")); + + this._listview = obj; + this._listview.name = this._getIdOfElement(this._listview.name); + if (!this._listview.format) { + if (typeof($) == 'function') { + for (var i = 0; i < this._tableRef._schema.length; i++) { + var fieldName = this._tableRef._schema[i].name; + + field = $("#" + this._listview.name + ' [dsid="' + fieldName + '"]'); + if (field && field.length == 1) { + field.html('{' + fieldName + '}'); + } + } + } + var text = document.getElementById(this._listview.name).innerHTML; + var pos = text.indexOf('
'; + progress.ui.UIHelper._defaultFieldTemplate = '
' + + '' + + '
'; + progress.ui.UIHelper._itemTemplate = progress.ui.UIHelper._defaultItemTemplate; + progress.ui.UIHelper._fieldTemplate = progress.ui.UIHelper._defaultFieldTemplate; + + progress.ui.UIHelper.setItemTemplate = function (template) { + progress.ui.UIHelper._itemTemplate = template ? template : progress.ui.UIHelper._defaultItemTemplate; + }; + + progress.ui.UIHelper.setFieldTemplate = function (template) { + progress.ui.UIHelper._fieldTemplate = + template ? template : progress.ui.UIHelper._defaultFieldTemplate; + }; + +})(); + +//this is so that we can see the code in Chrome's Source tab when script is loaded via XHR +//# sourceURL=progress.jsdo.js +/* +progress.session.js Version: 4.4.1-2 + +Copyright (c) 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + /* define these if not defined yet - they may already be defined if + progress.js was included first */ + if (typeof progress === "undefined") { + progress = {}; + } + if (typeof progress.data === "undefined") { + progress.data = {}; + } + + progress.data.ServicesManager = {}; + progress.data.ServicesManager._services = []; + progress.data.ServicesManager._resources = []; + progress.data.ServicesManager._data = []; + progress.data.ServicesManager._sessions = []; + progress.data.ServicesManager._jsdosessions = []; + /* + progress.data.ServicesManager.put = function(id, jsdo) { + progress.data.ServicesManager._data[id] = jsdo; + }; + progress.data.ServicesManager.get = function(id) { + return progress.data.ServicesManager._data[id]; + }; + */ + + progress.data.ServicesManager.addResource = function (id, resource) { + if (progress.data.ServicesManager._resources[id] === undefined) + progress.data.ServicesManager._resources[id] = resource; + else + throw new Error("A resource named '" + id + "' was already loaded."); + }; + progress.data.ServicesManager.getResource = function (id) { + return progress.data.ServicesManager._resources[id]; + }; + progress.data.ServicesManager.addService = function (id, service) { + if (progress.data.ServicesManager._services[id] === undefined) + progress.data.ServicesManager._services[id] = service; + else + throw new Error("A service named '" + id + "' was already loaded."); + }; + progress.data.ServicesManager.getService = function (id) { + return progress.data.ServicesManager._services[id]; + }; + progress.data.ServicesManager.addSession = function (catalogURI, session) { + if (progress.data.ServicesManager._sessions[catalogURI] === undefined) + progress.data.ServicesManager._sessions[catalogURI] = session; + else + throw new Error("Cannot load catalog '" + catalogURI + "' multiple times."); + }; + + progress.data.ServicesManager.addJSDOSession = function (catalogURI, jsdosession) { + if (progress.data.ServicesManager._jsdosessions[catalogURI] === undefined) { + progress.data.ServicesManager._jsdosessions[catalogURI] = jsdosession; + } + else { + throw new Error("Cannot load catalog '" + catalogURI + "' multiple times."); + } + }; + progress.data.ServicesManager.getSession = function (catalogURI) { + try { + return progress.data.ServicesManager._sessions[catalogURI]; + } + catch (e) { + return null; + } + }; + + progress.data.ServicesManager.cleanSession = function (session) { + var servicesKey, + resourcesKey, + sessionsKey, + service, + services = progress.data.ServicesManager._services, + resources = progress.data.ServicesManager._resources, + sessions = progress.data.ServicesManager._sessions, + jsdosessions = progress.data.ServicesManager._jsdosessions; + + // Delete the services and resources in the ServicesManager + // associated with the Session given + for (servicesKey in services) { + service = null; + if (services[servicesKey]._session === session) { + service = services[servicesKey]; + delete services[servicesKey]; + } + + if (!service) { + continue; + } + + for (resourcesKey in resources) { + if (resources[resourcesKey].service === service) { + delete resources[resourcesKey]; + } + } + } + + // Delete the session and jsdosession from the ServicesManager + for (sessionsKey in sessions) { + if (sessions[sessionsKey] === session) { + delete sessions[sessionsKey]; + + if(jsdosessions[sessionsKey]) { + delete jsdosessions[sessionsKey]; + } + } + } + }; + + /* + * Scans URL for parameters of the form {name} + * Returns array with the names + */ + function extractParamsFromURL(url) { + var urlParams = []; + if (typeof(url) == 'string') { + var paramName = null; + for (var i = 0; i < url.length; i++) { + if (url.charAt(i) == '{') { + paramName = ""; + } + else if (url.charAt(i) == '}') { + if (paramName) + urlParams.push(paramName); + paramName = null; + } + else if (paramName !== null) { + paramName += url.charAt(i); + } + } + } + return urlParams; + } + + /* + * Adds the catalog.json file provided by the catalog parameter, which is a JSDO + * that has loaded the catalog + */ + progress.data.ServicesManager.addCatalog = function (services, session) { + if (!services) { + throw new Error("Cannot find 'services' property in catalog file."); + } + if (services instanceof Array) { + + // first check if there are duplicates before we add them to our cache, + // which only handles unique values + for (var j = 0; j < services.length; j++) { + // don't allow services with the same name across sessions + if (progress.data.ServicesManager.getService(services[j].name) !== undefined) + throw new Error("A service named '" + services[j].name + "' was already loaded."); + + var resources = services[j].resources; + + if (resources instanceof Array) { + for (var i = 0; i < resources.length; i++) { + if (progress.data.ServicesManager.getResource(resources[i].name) !== undefined) + throw new Error("A resource named '" + resources[i].name + + "' was already loaded."); + } + } + else { + throw new Error("Missing 'resources' array in catalog."); + } + } + + for (var j = 0; j < services.length; j++) { + services[j]._session = session; + this.addService(services[j].name, services[j]); // Register the service + var resources = services[j].resources; + var baseAddress = services[j].address; + if (resources instanceof Array) { + for (var i = 0; i < resources.length; i++) { + var resource = resources[i]; + resource.fn = {}; + resource.service = services[j]; + resources[i].url = baseAddress + resources[i].path; + // Register resource + progress.data.ServicesManager.addResource(resources[i].name, resources[i]); + + // Process schema + resource.fields = null; + resource.primaryKeys = null; + if (resource.schema) { + resource.fields = {}; + resource.primaryKeys = {}; + resource._dataSetName = undefined; + resource._tempTableName = undefined; + var properties = null; + + try { + if (typeof resource.schema.properties != 'undefined') { + var keys = Object.keys(resource.schema.properties); + properties = resource.schema.properties; + if (keys.length == 1) { + if (typeof resource.schema.properties[keys[0]].properties != + 'undefined') { + // Schema corresponds to a DataSet + resource._dataSetName = keys[0]; + } + else if (typeof resource.schema.properties[keys[0]].items != + 'undefined') { + // Schema corresponds to a temp-table + resource.dataProperty = keys[0]; + properties = resource.schema.properties[keys[0]].items.properties; + resource._tempTableName = resource.dataProperty; + resource.primaryKeys[resource._tempTableName] = + resource.schema.properties[keys[0]].primaryKey; + } + } + } + else { + var keys = Object.keys(resource.schema); + if (keys.length == 1) { + resource.dataProperty = keys[0]; + if (typeof resource.schema[keys[0]].items != 'undefined') { + // Catalog format correspond to Table Schema + properties = resource.schema[keys[0]].items.properties; + resource._tempTableName = resource.dataProperty; + resource.primaryKeys[resource._tempTableName] = + resource.schema[keys[0]].primaryKey; + } + else if (typeof resource.schema[keys[0]].properties != 'undefined') { + // Catalog format correspond to DataSet Schema + resource._dataSetName = keys[0]; + resource.dataProperty = null; + properties = resource.schema; + } + } + } + } + catch (e) { + throw new Error("Error parsing catalog file."); + } + if (properties) { + if (resource._dataSetName) { + properties = properties[resource._dataSetName].properties; + for (var tableName in properties) { + resource.fields[tableName] = []; + resource.primaryKeys[tableName] = properties[tableName].primaryKey; + var tableProperties; + if (properties[tableName].items + && properties[tableName].items.properties) { + tableProperties = properties[tableName].items.properties; + } + else { + tableProperties = properties[tableName].properties; + } + for (var field in tableProperties) { + tableProperties[field].name = field; + if (field != '_id') + resource.fields[tableName].push(tableProperties[field]); + } + } + } + else { + var tableName = resource.dataProperty ? resource.dataProperty : ""; + resource.fields[tableName] = []; + for (var field in properties) { + properties[field].name = field; + if (field != '_id') + resource.fields[tableName].push(properties[field]); + } + } + } + else + throw new Error("Error parsing catalog file."); + } + else + resource.fields = null; + + // Validate relationship property + if ((resource.relations instanceof Array) + && resource.relations[0] + && resource.relations[0].RelationName) { + throw new Error( + "Relationship properties in catalog must begin with lowercase."); + } + // Process operations + resource.generic = {}; + if (resource.operations) { + for (var idx = 0; idx < resource.operations.length; idx++) { + if (resource.operations[idx].path) { + resource.operations[idx].url = + resource.url + resource.operations[idx].path; + } + else { + resource.operations[idx].url = resource.url; + } + if (!resource.operations[idx].params) { + resource.operations[idx].params = []; + } + if (!resource.operations[idx].type) { + resource.operations[idx].type = "INVOKE"; + } + + // Set opname - validation of opname is done later + var opname = resource.operations[idx].type.toLowerCase(); + + // Set default verb based on operation + if (!resource.operations[idx].verb) { + switch (opname) { + case 'create': + resource.operations[idx].verb = "POST"; + break; + case 'read': + resource.operations[idx].verb = "GET"; + break; + case 'update': + case 'invoke': + case 'submit': + case 'count': + resource.operations[idx].verb = "PUT"; + break; + case 'delete': + resource.operations[idx].verb = "DELETE"; + break; + default: + break; + } + } + + // Point fn to operations + var func = function fn(object, async) { + var deferred; + + // Add static variable fnName to function + if (typeof fn.fnName == 'undefined') { + fn.fnName = arguments[0]; // Name of function + fn.definition = arguments[1]; // Operation definition + return; + } + + var reqBody = null; + var url = fn.definition.url; + var jsdo = this; + var xhr = null; + + var request = {}; + if (object) { + if (typeof(object) != "object") { + throw new Error("Catalog error: Function '" + + fn.fnName + "' requires an object as a parameter."); + } + var objParam; + if (object instanceof XMLHttpRequest + || (object.constructor + && object.constructor.name === "XMLHttpRequest")) { + jsdo = object.jsdo; + xhr = object; + objParam = xhr.objParam; + + // use the request from the xhr request if possible + request = xhr.request; + } + else { + objParam = object; + } + + if (typeof async == 'undefined') { + async = this._async; + } + else { + async = Boolean(async); + } + + request.objParam = objParam; + + + // Process objParam + var isInvoke = (fn.definition.type.toUpperCase() == 'INVOKE'); + for (var i = 0; i < fn.definition.params.length; i++) { + var name = fn.definition.params[i].name; + switch (fn.definition.params[i].type) { + case 'PATH': + case 'QUERY': + case 'MATRIX': + var value = null; + if (objParam) + value = objParam[name]; + if (!value) + value = ""; + if (url.indexOf('{' + name + '}') == -1) { + throw new Error("Catalog error: Reference to " + + fn.definition.params[i].type + " parameter '" + + name + "' is missing in path."); + } + url = url.replace( + new RegExp('{' + name + '}', 'g'), + encodeURIComponent(value)); + break; + case 'REQUEST_BODY': + case 'REQUEST_BODY,RESPONSE_BODY': + case 'RESPONSE_BODY,REQUEST_BODY': + if (xhr && !reqBody) { + reqBody = objParam; + } + else { + var reqParam = objParam[name]; + if (isInvoke + && (fn.definition.params[i].xType + && ("DATASET,TABLE".indexOf( + fn.definition.params[i].xType) != -1))) { + var unwrapped = (jsdo._resource.service.settings + && jsdo._resource.service.settings.unwrapped); + if (unwrapped) { + // Remove extra level if found + if ((typeof(reqParam) == 'object') + && (Object.keys(reqParam).length == 1) + && (typeof(reqParam[name]) == 'object')) + reqParam = reqParam[name]; + } + else { + // Add extra level if not found + if ((typeof(reqParam) == 'object') + && (typeof(reqParam[name])=='undefined')){ + reqParam = {}; + reqParam[name] = objParam[name]; + } + } + } + if (!reqBody) { + reqBody = {}; + } + reqBody[name] = reqParam; + } + break; + case 'RESPONSE_BODY': + break; + default: + throw new Error("Catalog error: " + + "Unexpected parameter type '" + + fn.definition.params[i].type + "'."); + } + } + + // URL has parameters + if (url.indexOf('{') != -1) { + var paramsFromURL = extractParamsFromURL(url); + for (var i = 0; i < paramsFromURL.length; i++) { + var name = paramsFromURL[i]; + var value = null; + if (objParam) + value = objParam[name]; + if (!value) + value = ""; + if (typeof(value) === "object") { + value = JSON.stringify(value); + } + url = url.replace( + new RegExp('{' + name + '}', 'g'), + encodeURIComponent(value)); + } + } + } + + request.fnName = fn.fnName; + request.async = async; + + if (request.deferred === undefined && + typeof($) == 'function' && typeof($.Deferred) == 'function') { + deferred = $.Deferred(); + request.deferred = deferred; + } + + var data = jsdo._httpRequest(xhr, fn.definition.verb, + url, reqBody, request, async); + return data; + }; + // End of Function Definition + + switch (resource.operations[idx].verb.toLowerCase()) { + case 'get': + case 'post': + case 'put': + case 'delete': + break; + default: + throw new Error("Catalog error: Unexpected HTTP verb '" + + resource.operations[idx].verb + + "' found while parsing the catalog."); + } + + switch (opname) { + case 'invoke': + break; + case 'create': + case 'read': + case 'update': + case 'delete': + case 'submit': + case 'count': + if (typeof(resource.generic[opname]) == "function") { + throw new Error("Catalog error: Multiple '" + + resource.operations[idx].type + + "' operations specified in the catalog for resource '" + + resource.name + "'."); + } + else + resource.generic[opname] = func; + break; + default: + throw new Error("Catalog error: Unexpected operation '" + + resource.operations[idx].type + + "' found while parsing the catalog."); + } + + // Set fnName + var name = resource.operations[idx].name; + if (opname === "invoke" || opname === "count") { + resource.fn[name] = {}; + resource.fn[name]["function"] = func; + } + else { + name = "_" + opname; + } + func(name, resource.operations[idx]); + } + } + } + } + } + } + else { + throw new Error("Missing 'services' array in catalog."); + } + + }; + + /* + * Prints debug information about the ServicesManager. + */ + progress.data.ServicesManager.printDebugInfo = function (resourceName) { + if (resourceName) { + //console.log("** ServicesManager **"); + //console.log("** BEGIN **"); + var resource = progress.data.ServicesManager.getResource(resourceName); + if (resource) { + var cSchema = "Schema:\n"; + var cOperations = "Operations: " + resource.operations.length + "\n"; + for (var field in resource.schema.properties) { + cSchema += "\nName: " + field + + "\n"; + } + + for (var i = 0; i < resource.operations.length; i++) { + cOperations += "\n" + i + + "\nName: " + resource.operations[i].name + + "\nURL: " + resource.operations[i].url + + "\ntype: " + resource.operations[i].type + + "\nverb: " + resource.operations[i].verb + + "\nparams: " + resource.operations[i].params.length + + "\n"; + } + console.log("** DEBUG INFO **\nResource name: %s\nURL:%s\n%s\n%s\n\n", + resource.name, resource.url, cSchema, cOperations); + } + else + console.log("Resource not found"); + //console.log("** END **"); + } + }; + + + /* + * Contains information about a server-side Mobile service. + * Properties of args parameter for constructor: + * @param name the name of the service + * @param uri the URI of the service + */ + progress.data.MobileServiceObject = function MobileServiceObject(args) { + var _name = args.name; + Object.defineProperty(this, 'name', + { + get: function () { + return _name; + }, + enumerable: true + }); + + var _uri = args.uri; + Object.defineProperty(this, 'uri', + { + get: function () { + return _uri; + }, + enumerable: true + }); + }; + + /* + An object that maintains the X-CLIENT-PROPS header string + The data for the string is stored in the internal variable named contextObject and is + always up to date. The internal var contextString isn't created until the first time it's + needed (the first get of the contextHeader property), and then it's updated an cached + A call to setContext or setContextProperty updates contextObject but sets contextString to + null, which signals that it needs to be updated. If contextObject is an empty object, + contextString is set to undefined to indicate that no header is to be sent + */ + progress.data.ContextProperties = function() { + var contextObject = {}, + contextString; // if null, contextObject has been changed but string wasn't updated yet + + // the string to be sent in the X-CLIENT-PROPS header (unless Session.xClientProps has been set) + Object.defineProperty(this, 'contextHeader', + { + get: function () { + var header; + + if (contextString === null) { // needs to be updated + header = JSON.stringify( contextObject ); + if (header === "{}") { + contextString = undefined; + } + else { + contextString = header; + } + } + // else (contextString === undefined || has a usable value) + + return contextString; + }, + enumerable: true + }); + + /* determine whether the property is already present, and - + add it if it's not present + remove it if propertyValue is explicitly passed as undefined + otherwise replace its value (even if the new value is null or "") + */ + this.setContextProperty = function( propertyName, propertyValue) { + if (arguments.length < 2) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', + 'setContextProperty', 2)); + } + if (arguments.length !== 2) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", "Session", + "setContextProperty", 2)); + } + if (typeof propertyName !== "string") { + // {1}: Parameter {1} must be of type {3} in {4} call. + throw new Error(progress.data._getMsgText("jsdoMSG121", 'Session', 1, 'string', + 'setContextProperty')); + } + + if ( propertyValue === undefined ) { + delete contextObject[propertyName]; // OK if it doesn't exist -- no error + } + else { + contextObject[propertyName] = propertyValue; + } + contextString = null; // must be updated on next get of this.contextHeader + }; + + this.setContext = function( context ) { + var prop; + + if (arguments.length < 1) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'setContext', 1)); + } + if (arguments.length > 1) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'setContext', 1)); + } + if ( typeof context == "object" ) { + /* Copy the properties of the context passed in as an argument into + * an internal contextObject. (Note that if the context object passed in + * has a prototype, this code copies them, too) + */ + contextObject = {}; + for (prop in context) { + if( context.hasOwnProperty(prop) ) { + if (typeof context[prop] !== "function" ) { + contextObject[prop] = context[prop]; + } + } + } + } + else if ( (context === undefined) || (context === null) ) { + contextObject = {}; + } + else { + // {1}: Parameter {1} must be of type {3} in {4} call. + throw new Error(progress.data._getMsgText("jsdoMSG121", 'Session', 1, 'Object', + 'setContextProperty')); + } + contextString = null; // must be updated on next get of this.contextHeader + }; + + this.getContext = function( ) { + if (arguments.length > 0) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContext', 0)); + } + return contextObject; + }; + + this.getContextProperty = function( propertyName) { + if (arguments.length < 1) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContextProperty', 1)); + } + if (arguments.length > 1) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContextProperty', 1)); + } + return contextObject[propertyName]; + }; + + }; // end of ContextProperties + + /* + * Manages authentication and session ID information for a service. + * + * Use: OE mobile developer instantiates a session and calls addCatalog() to load + * information for one or more services defined in a catalog file. + * + * Developer instantiates JDSOs as needed. + * Usually all of the JSDOs will use the same session, but if a client-side + * service needs resources from more than one REST app, there would need to be more + * than one session + * + */ + progress.data.Session = function Session(options) { + + var defPropSupported = false; + if ((typeof Object.defineProperty) == 'function') { + defPropSupported = true; + } + + var that = this, + jsdosession, // "backpointer" if this Session is being used by a JSDOSession + isUserAgentiOS = false, // checked just below this var statement + isFirefox = false, // checked just below this var statement + isEdge = false, // checked just below this var statement + isIE = false, // checked just below this var statement + canPassCredentialsToOpenWithCORS = false, // False will always work if creds are correct + defaultiOSBasicAuthTimeout = 4000, + deviceIsOnline = true, // online until proven offline + restApplicationIsOnline = false, // was the Mobile Web Application that this Session object + // connects to online the last time it was checked? + // (value is always false if session is not logged in) + oepingAvailable = false, + defaultPartialPingURI = "/rest/_oeping", + partialPingURI = defaultPartialPingURI, + _storageKey, + _authProvider = null, + customCredentials = false, + + // Note: the variables above here are used during the lifetime of the object; the ones below + // are only used while the constructor is executing + storedAuthModel, + storedURI, + newURI, + stateWasReadFromStorage = false; + + // This is a hidden argument to suppress this warning and be re-used for future warnings + if (!options || options._silent !== true) { + console.warn("Session: As of JSDO 4.4, the Session object has been deprecated. Please use the JSDOSession object instead."); + } + + if (typeof navigator !== "undefined") { + if (typeof navigator.userAgent !== "undefined") { + isUserAgentiOS = navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)/i); + isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + // detect that we're running in MS Edge browser + isEdge = navigator.userAgent.indexOf('Edge/') > -1; + // detect that we're running in IE 11 (or IE 11 in pre-11 mode) or IE 10 browser + isIE = ( (navigator.userAgent.indexOf('Trident/')) > -1 || (navigator.userAgent.indexOf('MSIE 10') > -1)); + } + } + + // Firefox, Edge, and IE will throw an error on the send() if CORS is being used for the request + // and we have included credentials in the URI (which is what passing them to open() does), + canPassCredentialsToOpenWithCORS = !(isFirefox || isEdge || isIE); + + // When using basic authentication, we can pass the user name and password to the XMLHttpRequest.open() + // method. However, in some browsers, passing credentials to open() will result in the xhr's .send() + // method throwing an error. The goal of this function is to figure out whether it's safe to include + // the credentials. It returns false if there could be a problem, true otherwise. + // Note: currently it does this solely on the basis of what browser we are running in, regardless + // of whether the request will actually use the CORS protocol. Ideally, we should take into account whether + // the request will actually require CORS. The question is whether we can reliably do that. + // The reason for taking the specific request into account is that there are drawbacks to not passing the + // credentials when we are NOT using CORS, namely that if the credentials are invalid, some browsers will + // put up their own prompt for credentials in non-CORS situations (those browsers are IE, Edge, and Chrome) + function canPassCredentialsToOpen() { + return canPassCredentialsToOpenWithCORS; + } + + this._onlineHandler = function () { + setDeviceIsOnline(true); + that.trigger("online", that, null); + }; + + this._offlineHandler = function () { + setDeviceIsOnline(false); + that.trigger("offline", that, progress.data.Session.DEVICE_OFFLINE, null); + }; + + if ((typeof window != 'undefined' ) && (window.addEventListener)) { + window.addEventListener("online", this._onlineHandler, false); + window.addEventListener("offline", this._offlineHandler, false); + } + + /* constants and properties - define them as properties via the defineProperty() + * function, which has "writable" and "configurable" parameters that both + * default to false, so these calls create properties that are read-only + * + * IF WE DECIDE THAT WE CAN ASSUME WE ALWAYS RUN WITH A VERSION OF JAVASCRIPT THAT SUPPORTS + * Object.DefineProperty(), WE CAN DELETE THE defPropSupported VARIABLE, THE TEST OF IT BELOW, + * AND THE 'ELSE' CLAUSE BELOW AND ALL THE setXxxx functions (AND CHANGE THE CALLS TO THE setXxxx + * FUNCTIONS SO THEY JUST REFER TO THE PROPERTY) + * + */ + + // define these unconditionally so we don't get a warning on the push calls that they might + // have been uninitialized + var _catalogURIs = []; + var _services = []; + var _jsdos = []; + + this.onOpenRequest = null; + + var _password = null; + + if (defPropSupported) { + var _userName = null; + Object.defineProperty(this, 'userName', + { + get: function () { + return _userName; + }, + enumerable: true + }); + + var _loginTarget = '/static/home.html'; + Object.defineProperty(this, 'loginTarget', + { + get: function () { + return _loginTarget; + }, + enumerable: true + }); + + var _serviceURI = null; + Object.defineProperty(this, 'serviceURI', + { + get: function () { + return _serviceURI; + }, + enumerable: true + }); + + Object.defineProperty(this, 'catalogURIs', + { + get: function () { + return _catalogURIs; + }, + enumerable: true + }); + + Object.defineProperty(this, 'services', + { + get: function () { + return _services; + }, + enumerable: true + }); + + var _loginResult = null; + Object.defineProperty(this, 'loginResult', + { + get: function () { + return _loginResult; + }, + enumerable: true + }); + + var _loginHttpStatus = null; + Object.defineProperty(this, 'loginHttpStatus', + { + get: function () { + return _loginHttpStatus; + }, + enumerable: true + }); + + var _clientContextId = null; + Object.defineProperty(this, 'clientContextId', + { + get: function () { + return _clientContextId; + }, + enumerable: true + }); + + var _authenticationModel = progress.data.Session.AUTH_TYPE_ANON; + Object.defineProperty(this, 'authenticationModel', + { + get: function () { + return _authenticationModel; + }, + set: function (newval) { + if (newval) { + newval = newval.toLowerCase(); + } + switch (newval) { + case progress.data.Session.AUTH_TYPE_FORM : + case progress.data.Session.AUTH_TYPE_BASIC : + case progress.data.Session.AUTH_TYPE_ANON : + case progress.data.Session.AUTH_TYPE_SSO : + case null : + _authenticationModel = newval; + storeSessionInfo("authenticationModel", newval); + break; + default: + throw new Error("Error setting Session.authenticationModel. '" + + newval + "' is an invalid value."); + } + }, + enumerable: true + }); + + var _lastSessionXHR = null; + Object.defineProperty(this, 'lastSessionXHR', + { + get: function () { + return _lastSessionXHR; + }, + enumerable: true + }); + + Object.defineProperty(this, 'connected', + { + get: function () { + return (this.loginResult === progress.data.Session.LOGIN_SUCCESS) + && restApplicationIsOnline + && deviceIsOnline; + }, + enumerable: true + }); + + Object.defineProperty(this, 'JSDOs', + { + get: function () { + return _jsdos; + }, + enumerable: true + }); + + var _pingInterval = 0; + var _timeoutID = null; + Object.defineProperty(this, 'pingInterval', + { + get: function () { + return _pingInterval; + }, + set: function (newval) { + if ( (typeof newval === "number") && (newval >= 0) ) { + _pingInterval = newval; + storeSessionInfo("pingInterval", newval); + if (newval > 0) { + // if we're logged in, start autopinging + if (this.loginResult === progress.data.Session.LOGIN_SUCCESS) { + _timeoutID = setTimeout(this._autoping, newval); + } + } + else if (newval === 0) { + clearTimeout(_timeoutID); + _pingInterval = 0; + } + } + else { + throw new Error("Error setting Session.pingInterval. '" + + newval + "' is an invalid value."); + } + }, + enumerable: true + }); + + var _contextProperties = new progress.data.ContextProperties(); + Object.defineProperty( this, + "_contextProperties", + { + get: function () { + return _contextProperties; + }, + enumerable: false + } + ); + + var isInvalidated = false; + Object.defineProperty( + this, + "_isInvalidated", + { + get: function () { + return isInvalidated; + }, + enumerable: false + } + ); + + // used internally, not supported as part of the Session API (tho authProvider is part + // of the *JSDOSession* API) + Object.defineProperty( this, + "_authProvider", + { + get: function () { + return _authProvider; + }, + set: function(newval) { + if (_authProvider) { + throw new Error("Internal Error setting Session._authProvider. '" + + "The property has already been set."); + + } else { + setAuthProvider(newval); + } + }, + enumerable: false + } + ); + } + else { + this.userName = null; + this.loginTarget = '/static/home.html'; + this.serviceURI = null; + this.catalogURIs = []; + this.services = []; + this.loginResult = null; + this.loginHttpStatus = null; + this.clientContextId = null; + this.authenticationModel = progress.data.Session.AUTH_TYPE_ANON; + this.lastSessionXHR = null; + } + + // stores data value using the JSDOSession's storage key plus the infoName + // argument as a key. If there is no infoName, just uses the storage key + // by itself (the latter case is intended to serve as a flag that we have + // stored this JSDOSession's data before) + // + function storeSessionInfo(infoName, value) { + var key; + if (that.loginResult === progress.data.Session.LOGIN_SUCCESS && + typeof (sessionStorage) === 'object' && _storageKey) { + + key = _storageKey; + if (infoName) { + key = key + "." + infoName; + } + if (typeof (value) !== 'undefined') { + sessionStorage.setItem(key, JSON.stringify(value)); + } + } + } + + function retrieveSessionInfo(infoName) { + var key, + jsonStr, + value = null; + if (typeof (sessionStorage) === 'object' && _storageKey) { + key = _storageKey; + if (infoName) { + key = key + "." + infoName; + } + jsonStr = sessionStorage.getItem(key); + if (jsonStr !== null) { + try { + value = JSON.parse(jsonStr); + } catch (e) { + value = null; + } + } + return value; + } + } + + function clearSessionInfo(infoName) { + var key; + if (typeof (sessionStorage) === 'object' && _storageKey) { + key = _storageKey; + if (infoName) { + key = key + "." + infoName; + sessionStorage.removeItem(key); + } + } + } + + function storeAllSessionInfo() { + if (_storageKey) { + storeSessionInfo("loginResult", that.loginResult); + storeSessionInfo("userName", that.userName); + storeSessionInfo("serviceURI", that.serviceURI); + storeSessionInfo("loginHttpStatus", that.loginHttpStatus); + storeSessionInfo("authenticationModel", that.authenticationModel); + storeSessionInfo("pingInterval", that.pingInterval); + storeSessionInfo("oepingAvailable", oepingAvailable); + storeSessionInfo("partialPingURI", partialPingURI); + storeSessionInfo("clientContextId", that.clientContextId); + storeSessionInfo("deviceIsOnline", deviceIsOnline); + storeSessionInfo("restApplicationIsOnline", restApplicationIsOnline); + if (that._authProvider) { + storeSessionInfo("_authProvider.init", + {uri: that._authProvider.uri, + authenticationModel: that._authProvider.authenticationModel}); + } + storeSessionInfo(_storageKey, true); + } + } + + function clearAllSessionInfo() { + if (_storageKey) { + if (retrieveSessionInfo(_storageKey)) { + clearSessionInfo("loginResult"); + clearSessionInfo("userName"); + clearSessionInfo("serviceURI"); + clearSessionInfo("loginHttpStatus"); + clearSessionInfo("clientContextId"); + clearSessionInfo("deviceIsOnline"); + clearSessionInfo("restApplicationIsOnline"); + clearSessionInfo("authenticationModel"); + clearSessionInfo("pingInterval"); + clearSessionInfo("oepingAvailable"); + clearSessionInfo("partialPingURI"); + clearSessionInfo("_authProvider.init"); + clearSessionInfo(_storageKey); + } + } + } + + function setSessionInfoFromStorage(key) { + var authproviderInitObject, + authProvider; + if (retrieveSessionInfo(key)) { + setLoginResult(retrieveSessionInfo("loginResult"), this); + setUserName(retrieveSessionInfo("userName"), this); + setServiceURI(retrieveSessionInfo("serviceURI"), this); + setLoginHttpStatus(retrieveSessionInfo("loginHttpStatus"), this); + setClientContextID(retrieveSessionInfo("clientContextId"), this); + setDeviceIsOnline(retrieveSessionInfo("deviceIsOnline")); + setRestApplicationIsOnline(retrieveSessionInfo("restApplicationIsOnline")); + that.authenticationModel = retrieveSessionInfo("authenticationModel"); + that.pingInterval = retrieveSessionInfo("pingInterval"); + setOepingAvailable(retrieveSessionInfo("oepingAvailable")); + setPartialPingURI(retrieveSessionInfo("partialPingURI")); + // if information on an AuthenticationProvider for the session is in storage, and if + // the authProvider hasn't already been set for this Session, create a new authProvider + // using the same info as the old one. This would be likely to happen if the app's code + // had used the old JSDOSession.login API, where we create the AuthenticationProvider + // automatically during login instead of the code passing one to the constructor + if (!that._authProvider) { + authproviderInitObject = retrieveSessionInfo("_authProvider.init"); + if (authproviderInitObject) { + setAuthProvider(new progress.data.AuthenticationProvider(authproviderInitObject)); + } + } + } + } + + function setUserName(newname, sessionObject) { + if (defPropSupported) { + _userName = newname; + } + else { + sessionObject.userName = newname; + } + + storeSessionInfo("userName", newname); + } + + function setLoginTarget(target, sessionObject) { + if (defPropSupported) { + _loginTarget = target; + } + else { + sessionObject.loginTarget = target; + } + } + + function setServiceURI(url, sessionObject) { + if (defPropSupported) { + _serviceURI = url; + } + else { + sessionObject.serviceURI = url; + } + + storeSessionInfo("serviceURI", url); + } + + function pushCatalogURIs(url, sessionObject) { + if (defPropSupported) { + _catalogURIs.push(url); + } + else { + sessionObject.catalogURIs.push(url); + } + } + + function pushService(serviceObject, sessionObject) { + if (defPropSupported) { + _services.push(serviceObject); + } + else { + sessionObject.services.push(serviceObject); + } + } + + function findService(serviceName) { + for (var prop in _services) { + var srv = _services[prop]; + if (srv.name === serviceName) { + return srv; + } + } + return null; + } + + function setLoginResult(result, sessionObject) { + if (defPropSupported) { + _loginResult = result; + } else { + sessionObject.loginResult = result; + } + + if (result === progress.data.Session.LOGIN_SUCCESS) { + storeSessionInfo("loginResult", result); + } else { + // Let's clear sessionStorage since we logged out or something went bad! + clearAllSessionInfo(); + } + } + + function setLoginHttpStatus(status, sessionObject) { + if (defPropSupported) { + _loginHttpStatus = status; + } + else { + sessionObject.loginHttpStatus = status; + } + + storeSessionInfo("loginHttpStatus", status); + } + + function setClientContextIDfromXHR(xhr, sessionObject) { + if (xhr) { + setClientContextID(getResponseHeaderNoError(xhr, "X-CLIENT-CONTEXT-ID"), sessionObject); + } + } + + function setClientContextID(ccid, sessionObject) { + if (defPropSupported) { + _clientContextId = ccid; + } + else { + sessionObject.clientContextId = ccid; + } + + storeSessionInfo("clientContextId", ccid); + } + + function setLastSessionXHR(xhr, sessionObject) { + if (defPropSupported) { + _lastSessionXHR = xhr; + } + else { + sessionObject.lastSessionXHR = xhr; + } + } + + function setDeviceIsOnline(value) { + deviceIsOnline = value; + + storeSessionInfo("deviceIsOnline", value); + } + + function setAuthProvider(value) { + // Do this to preserve authprovider's null-ness. + _authProvider = value ? value : null; + } + + function setRestApplicationIsOnline(value) { + restApplicationIsOnline = value; + + storeSessionInfo("restApplicationIsOnline", value); + } + + function setOepingAvailable(value) { + oepingAvailable = value; + + storeSessionInfo("oepingAvailable", value); + } + + function setPartialPingURI(value) { + partialPingURI = value; + + storeSessionInfo("partialPingURI", value); + } + + /* + When using CORS, if the client asks for a response header that is not among + the headers exposed by the Web application, the user agent may write an error + to the console, e.g., "REFUSED TO GET UNSAFE HEADER". This function checks for + a given response header in a way that will avoid the error message. It does this + by requesting all headers and then checking to see whether the desired header + is present (it will not be present, even if the server sent it, if the server has not + also allowed that header). The function caches the string returned by getAllResponseHeaders + by storing it on the xhr that was used in the request. It does the caching in + case there is another header to be checked. + */ + function getResponseHeaderNoError(xhr, headerName) { + var allHeaders = xhr._pdsResponseHeaders, + regExp; + + if (allHeaders === undefined) { + allHeaders = xhr.getAllResponseHeaders(); + if ( allHeaders ) { + xhr._pdsResponseHeaders = allHeaders; + } + else { + xhr._pdsResponseHeaders = null; + } + } + if ( allHeaders ) { + regExp = new RegExp("^" + headerName + ":", "m"); + if ( allHeaders.match(regExp) ) { + return xhr.getResponseHeader(headerName); + } + } + + return null; + } + + // "Methods" + + this._pushJSDOs = function (jsdo) { + _jsdos.push(jsdo); + }; + + + /* _openRequest (intended for progress.data library use only) + * calls open() for an xhr -- the assumption is that this is an xhr for a JSDO, and we need to add + * some session management information for the request, such as user credentials and a session ID if + * there is one + * + * The callback parameter is to support async calls --- it's possible that the call in here to + * _openRequestAndAuthorize will make an async request (for token refresh), so it's expected that + * callers will invoke _openRequest with a callback parameter for async execution + */ + this._openRequest = function (xhr, verb, url, async, callback) { + var urlPlusCCID, + that = this; + + function afterOpenAndAuthorize(xhr) { + // add CCID header + if (that.clientContextId && (that.clientContextId !== "0")) { + xhr.setRequestHeader("X-CLIENT-CONTEXT-ID", that.clientContextId); + } + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(that, xhr); + + if (typeof that.onOpenRequest === 'function') { + var params = { + "xhr": xhr, + "verb": verb, + "uri": urlPlusCCID, + "async": async, + "formPreTest": false, + "session": that + }; + that.onOpenRequest(params); + // xhr = params.xhr; //Note that, currently, this would have no effect in the caller. + } + if (callback) { + callback(); + } + } + + if (this._isInvalidated) { + // Session: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "Session")); + } + + if (this.loginResult !== progress.data.Session.LOGIN_SUCCESS && !this._authProvider && this.authenticationModel) { + throw new Error("Attempted to make server request when there is no active session."); + } + + // if resource url is not absolute, add the REST app url to the front + urlPlusCCID = this._prependAppURL(url); + + // add CCID as JSESSIONID query string to url + urlPlusCCID = this._addCCIDtoURL(urlPlusCCID); + + // add time stamp to the url + if (progress.data.Session._useTimeStamp) { + urlPlusCCID = progress.data.Session._addTimeStampToURL(urlPlusCCID); + } + + // should be able to remove this check and only do what's in the "if" when we no longer + // support calling the Session API directly (need to keep that now because tdriver, for + // one, uses the Session object, and uses it synchronously + if (this._authProvider) { + this._authProvider._openRequestAndAuthorize(xhr, + verb, + urlPlusCCID, + async, + afterOpenAndAuthorize); + } else { + this._setXHRCredentials(xhr, verb, urlPlusCCID, this.userName, _password, async); + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + _addWithCredentialsAndAccept(xhr, "application/json"); + } + afterOpenAndAuthorize(xhr); + } + + }; + + // callback used in login to determine whether ping is available on server + this.pingTestCallback = function (cbArgs) { + var foundOeping = cbArgs.pingResult ? true : false; + + setOepingAvailable(foundOeping); + }; + + // generic async callback, currently used by login(), addCatalog(), logout(), connect, and disconnect + this._onReadyStateChangeGeneric = function () { + var xhr = this; + var result; + var errorObject; + + clearTimeout(xhr._requestTimeout); // for the iOS Basic Auth bug + + if (xhr.readyState == 4) { + result = null; + errorObject = null; + + // initial processing of the response from the Web application + if ((typeof xhr.onResponseFn) == 'function') { + try { + result = xhr.onResponseFn(xhr); + // ( note that result will remain null if this is a logout() ) + } + catch (e) { + errorObject = e; + } + } + // handle the results of the processing (e.g., fire any events required) + if ((typeof xhr.onResponseProcessedFn) == 'function') { + if (!result) { + result = progress.data.Session.GENERAL_FAILURE; + } + xhr.onResponseProcessedFn(xhr.pdsession, result, errorObject, xhr); + } + } + }; + + // Intended only for internal use by the JSDO library + // NOTE: disconnect does not currently send a request to the Web application for the Anonymous or + // OE SSO models. It's conceivable, though unlikely, that it might. For that reason, the design is + // similar to the functions that DO make a server request. There is a "setup" function (this one) + // and a separate function to process the "result" (_processDisconnectResult, below). Currently the + // setup function is minimal and just calls _processDisconnectResult directly. If we ever do need to + // send a server request, _processDisconnectResult will be specified as the callback to be invoked + // from onReadyStateChangeGeneric. The possibility of this potential enhancement is the reason for + // the odd signature of _processDisconnectResult, which has a currently unused first parameter for + // the potential XHR. + this._disconnect = function (deferred) { + + // Note: we use the "no harm, no foul" approach for disconnect. If you aren't connected, it's + // regarded as a success rather than cause for throwing an error. + this._processDisconnectResult(null, deferred); + }; + + + // This is separate from _disconnect for cases in which _disconnect makes a server request. + // If there has been a server request, xhr should be valid and deferred will be undefined + // If there was no server request, xhr will be undefined and deferred will be valid. + // If this needs to be enhanced to support server requests, see _procesLoginResponse as + // a general model + // Probably the only time this function will be called as the result of a server request is with + // Form authentication, and even then it's questionable + this._processDisconnectResult = function (xhr, deferred) { + + this._reinitializeAfterLogout(this, progress.data.Session.SUCCESS); + this._disconnectComplete(this, progress.data.Session.SUCCESS, null, null, deferred); + }; + + this._disconnectComplete = function (pdsession, result, errObj, xhr, deferred) { + pdsession.trigger("afterDisconnect", pdsession, result, errObj, xhr, deferred); + }; + + + // GET RID OF progress.data.Session login CODE (AND RELATED) IF WE DROP SUPPORT FOR USING + // THE OLD progress.data.Session API DIRECTLY (mainly a problem for existing code (tdriver), + // or anyone who wants to call methods synchronously, which should be no one) + /* login + * + */ + + // store password here until successful login; only then do we store it in the Session object + var pwSave = null; + // store user name here until successful login; only then do we store it in the Session object + var unameSave = null; + this.login = function (serviceURI, loginUserName, loginPassword, loginTarget) { + var uname, + pw, + isAsync = false, + args = [], + deferred, + iOSBasicAuthTimeout, + uriForRequest; // "decorated" version of serviceURI, used to actually send the request + + pwSave = null; // in case these are left over from a previous login + unameSave = null; + + if (!defPropSupported) { + // this is here on the presumably slim chance that we're running with a + // version of JavaScript that doesn't support defineProperty (otherwise + // the lower casing will have already happened). When we decide that it's + // OK to remove our conditionalization of property definitions, we should + // get rid of this whole conditional + this.authenticationModel = this.authenticationModel.toLowerCase(); + } + + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // Session: Cannot call login() when authenticationModel is SSO. + // Please use the AuthenticationProvider object instead. + throw new Error(progress.data._getMsgText("jsdoMSG057", 'Session', 'login()')); + } + + if (this.loginResult === progress.data.Session.LOGIN_SUCCESS || this._authProvider) { + throw new Error("Attempted to call login() on a Session object that is already logged in."); + } + + if (arguments.length > 0) { + if (arguments[0] && typeof(arguments[0]) === 'object') { + // Note that arguments[0].serviceURI may be undefined because when the JSDOSession + // uses a Session internally, it passes serviceURI to the constructor. The other + // properties may be present, though + args[0] = arguments[0].serviceURI; + args[1] = arguments[0].userName; + args[2] = arguments[0].password; + args[3] = arguments[0].loginTarget; + args[4] = arguments[0].async; + + /* Special for JSDOSession: if this method was called by a JSDOSession object, + it passes deferred and jsdosession and we need to eventually attach them + to the XHR we use so that the promise created by the JSDOSession will work + correctly + */ + deferred = arguments[0].deferred; + + iOSBasicAuthTimeout = arguments[0].iOSBasicAuthTimeout; + if ( typeof iOSBasicAuthTimeout === 'undefined' ) { + iOSBasicAuthTimeout = defaultiOSBasicAuthTimeout; + } + else if (iOSBasicAuthTimeout && (typeof iOSBasicAuthTimeout != 'number')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'login', + 'The iOSBasicAuthTimeout argument was invalid.')); + } + } + else { + args = arguments; + } + } + + if (args.length > 0) { + if (args[0]) { + var restURLtemp = args[0]; + + // get rid of trailing '/' because appending service url that starts with '/' + // will cause request failures + if (restURLtemp[restURLtemp.length - 1] === "/") { + restURLtemp = restURLtemp.substring(0, restURLtemp.length - 1); + } + setServiceURI(restURLtemp, this); + } else if (!this.serviceURI) { + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); + throw new Error("Session.login() is missing the serviceURI argument."); + } + + if (args[1]) { + uname = args[1]; + } + + if (args[2]) { + pw = args[2]; + } + + if (args[3]) { + setLoginTarget(args[3], this); + } + + if (args[4]) { + if (typeof(args[4]) === 'boolean') { + isAsync = args[4]; + } + else { + throw new Error("Session.login() was passed an async setting that is not a boolean."); + } + } + } + else { + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); + throw new Error("Session.login() is missing the serviceURI argument."); + } + + // use these temp cred variables later; if login succeeds, we'll use them to set the + // real credentials + unameSave = uname; + pwSave = pw; + + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_ANON || + this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + /* anonymous should NOT have a username and password passed (this is + probably unnecessary because the XHR seems to send the request without + credentials first, then intercept the 401 if there is one and try again, + this time with credentials. Just making sure. + */ + /* For form authentication, we may as well not send the user name and password + * on this request, since we are just trying to test whether the authentication + * has already happened and they are therefore irrelevant + */ + uname = null; + pw = null; + } + + var xhr = new XMLHttpRequest(); + xhr.pdsession = this; + + try { + uriForRequest = this.serviceURI + this.loginTarget; + if (progress.data.Session._useTimeStamp) { + uriForRequest = progress.data.Session._addTimeStampToURL(uriForRequest); + } + this._setXHRCredentials(xhr, 'GET', uriForRequest, uname, pw, isAsync); + + progress.data.Session._setNoCacheHeaders(xhr); + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(this, xhr); + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + _addWithCredentialsAndAccept(xhr, + "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + } + + xhr._isAsync = isAsync; + if (isAsync) { + xhr.onreadystatechange = this._onReadyStateChangeGeneric; + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + xhr.onResponseFn = this._afterFormPretestLogin; + } + else { + xhr.onResponseFn = this._processLoginResult; + xhr.onResponseProcessedFn = this._loginComplete; + } + if ( this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC + && isUserAgentiOS + && iOSBasicAuthTimeout > 0 ) { + xhr._requestTimeout = setTimeout( function (){ + clearTimeout(xhr._requestTimeout); + xhr._iosTimeOutExpired = true; + xhr.abort(); + }, + iOSBasicAuthTimeout); + } + xhr._jsdosession = jsdosession; // in case the caller is a JSDOSession + xhr._deferred = deferred; // in case the caller is a JSDOSession + } + + if (typeof this.onOpenRequest === 'function') { + var isFormPreTest = false; + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + isFormPreTest = true; + } + + // set this here in case onOpenRequest checks it + setLastSessionXHR(xhr, this); + var params = { + "xhr": xhr, + "verb": "GET", + "uri": this.serviceURI + this.loginTarget, + "async": false, + "formPreTest": isFormPreTest, + "session": this + }; + this.onOpenRequest(params); + xhr = params.xhr; // just in case it has been changed + } + setLastSessionXHR(xhr, this); + xhr.send(null); + } + catch (e) { + clearTimeout(xhr._requestTimeout); + setLoginHttpStatus(xhr.status, this); + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); + unameSave = null; + pwSave = null; + throw e; + } + + if (isAsync) { + return progress.data.Session.ASYNC_PENDING; + } + else { + setLoginHttpStatus(xhr.status, this); + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + return (this._afterFormPretestLogin(xhr) ); + } + else { + return (this._processLoginResult(xhr) ); + } + } + }; + + + this._afterFormPretestLogin = function (xhr) { + var pdsession = xhr.pdsession; + setLoginHttpStatus(xhr.status, xhr.pdsession); + + var formLoginParams = { + "xhr": xhr, + "pw": pwSave, + "uname": unameSave, + "theSession": pdsession + }; + try { + return doFormLogin(formLoginParams); + } + catch (e) { + pwSave = null; + unameSave = null; + throw e; + } + }; + + /* doFormLogin + * This function handles logging in to a service that uses form-based authentication. It's separate + * from the main login function because it's long. One of the things it does is examine the + * response from an initial attempt to get the login target without credentials (done in the main + * login() function) to determine whether the user has already been authenticated. Although a + * current OE Mobile Web application (as of 5/30/2013) will return an error if authentication + * failed on a form login, previous versions and non-OE servers return a + * redirect to a login page and the user agent (browser or native wrapper) + * usually then fetches the redirect location and returns it along with a + * 200 Success status, when in fcat it was an authentication failure. Hence + * the need to analyze the response to try to figure out what we get back. + * + */ + function doFormLogin(args) { + var xhr = args.xhr; + var theSession = args.theSession; + var oldXHR; + + // check whether we got the OE REST Form based error response + var contentType = null; + var needAuth = false; + var params = { + "session": theSession, + "xhr": xhr, + "statusFromjson": null + }; + + contentType = xhr.getResponseHeader("Content-Type"); + + if (contentType && contentType.indexOf("application/json") >= 0) { + handleJSONLoginResponse(params); + if ( !params.statusFromjson + || (params.statusFromjson >= 400 && params.statusFromjson < 500) + ) { + needAuth = true; + } + else { + // either the response shows that we're already authenticated, or + // there's some error other than an authentication error + setLoginHttpStatus(params.statusFromjson, theSession); + } + } + else { + // need to do only 200 for async to work with MWA down + if (theSession.loginHttpStatus == 200) { + if (_gotLoginForm(xhr)) { + needAuth = true; + } + // else we are assuming we truly retrieved the login target and + // therefore we were previously authenticated + } + // else had an error, just return it + } + + if (needAuth) { + // create new XHR, because if this is an async call we don't want to + // confuse things by using this xhr to send another request while we're + // still processing its old request (this function, doFormLogin(), may + // have been called from onReadyStateChangeGeneric and it's conceivable + // that that function has more code to execute involving this xhr) + oldXHR = xhr; + xhr = new XMLHttpRequest(); + args.xhr = xhr; + params.xhr = xhr; + + // need to transfer any properties that the Session code stored in the + // the xhr that need to persist across the 2 requests made by a our + // login implementation for Form auth + xhr.pdsession = oldXHR.pdsession; + xhr._isAsync = oldXHR._isAsync; + xhr._deferred = oldXHR._deferred; // special for JSDOSession + xhr._jsdosession = oldXHR._jsdosession; // special for JSDOSession + + xhr.open('POST', theSession.serviceURI + "/static/auth/j_spring_security_check",xhr._isAsync); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + xhr.setRequestHeader("Cache-Control", "max-age=0"); + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(theSession, xhr); + + _addWithCredentialsAndAccept(xhr, "application/json"); + + try { + + // Note: this gives a developer a way to change certain aspects of how we do the + // form-based login, but we will still be assuming that we are going directly to + // j_spring_security_check and including credentials in the body. They really should not + // try to change that. + // + if (typeof theSession.onOpenRequest === 'function') { + var cbparams = { + "xhr": xhr, + "verb": "POST", + "uri": theSession.serviceURI + "/static/auth/j_spring_security_check", + "async": xhr._isAsync, + "formPreTest": false, + "session": theSession + }; + theSession.onOpenRequest(cbparams); + xhr = cbparams.xhr; + } + + if (xhr._isAsync) { + xhr.onreadystatechange = theSession._onReadyStateChangeGeneric; + xhr.onResponseFn = theSession._afterFormLogin; + xhr.onResponseProcessedFn = theSession._loginComplete; + } + + // j_username=username&j_password=password&submit=Submit + xhr.send("j_username=" + encodeURIComponent(args.uname) + "&j_password=" + encodeURIComponent(args.pw) + "&submit=Submit"); + } + catch (e) { + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, theSession); + setLoginHttpStatus(xhr.status, theSession); + // null the temporary credentials variables + unameSave = null; + pwSave = null; + throw e; + } + + } + + if (xhr._isAsync && !needAuth) { + xhr.onResponseProcessedFn = theSession._loginComplete; + return theSession._afterFormLogin(xhr); + } + if (!xhr._isAsync) { + return theSession._afterFormLogin(xhr); + } + + } + + this._afterFormLogin = function (xhr) { + // check what we got + var theSession = xhr.pdsession; + var params = { + "session": theSession, + "xhr": xhr, + "statusFromjson": null + }; + var contentType = xhr.getResponseHeader("Content-Type"); + + if (contentType && contentType.indexOf("application/json") >= 0) { + handleJSONLoginResponse(params); + if (!params.statusFromjson) { + throw new Error( + "Internal OpenEdge Mobile client error handling login response. HTTP status: " + + xhr.status + "."); + } + else { + setLoginHttpStatus(params.statusFromjson, theSession); + } + } + else { + if (xhr.status === 200) { + // Was the response actually the login failure page or the login page itself (in case + // the appSecurity config file sets the login failure url so the server sends the login + // page again)? If so, call it an error because the credentials apparently failed to be + // authenticated + if (_gotLoginFailure(xhr) || _gotLoginForm(xhr)) { + setLoginHttpStatus(401, theSession); + } + else { + setLoginHttpStatus(xhr.status, theSession); + } + } + } + + return theSession._processLoginResult(xhr); + }; + + + this._processLoginResult = function (xhr) { + /* OK, one way or another, by hook or by crook, the Session object's loginHttpStatus + * has been set to the value that indicates the real outcome of the + * login, after adjusting for form-based authentication and anything + * else. At this point, it should be just a matter of examining + * this.loginHttpStatus, using it to set this.loginResult, maybe doing + * some other work appropriate to the outcome of the login, and returning + * this.loginResult. + */ + var pdsession = xhr.pdsession; + + setLoginHttpStatus(xhr.status, xhr.pdsession); + + if (pdsession.loginHttpStatus === 200) { + setLoginResult(progress.data.Session.LOGIN_SUCCESS, pdsession); + setRestApplicationIsOnline(true); + setUserName(unameSave, pdsession); + _password = pwSave; + pdsession._saveClientContextId(xhr); + storeAllSessionInfo(); // save info to persistent storage + + var pingTestArgs = { + pingURI: null, async: true, onCompleteFn: null, + fireEventIfOfflineChange: true, onReadyStateFn: pdsession._pingtestOnReadyStateChange + }; + pingTestArgs.pingURI = pdsession._makePingURI(); + pdsession._sendPing(pingTestArgs); // see whether the ping feature is available + } + else { + if (pdsession.loginHttpStatus == 401) { + setLoginResult(progress.data.Session.LOGIN_AUTHENTICATION_FAILURE, pdsession); + } + else { + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, pdsession); + } + } + setLastSessionXHR(xhr, pdsession); + updateContextPropsFromResponse(pdsession, xhr); + + // null the temporary credentials variables + unameSave = null; + pwSave = null; + if (xhr._iosTimeOutExpired) { + throw new Error( progress.data._getMsgText("jsdoMSG047", "login") ); + } + + // return loginResult even if it's an async operation -- the async handler + // (e.g., onReadyStateChangeGeneric) will just ignore + return pdsession.loginResult; + }; + + + this._loginComplete = function (pdsession, result, errObj, xhr) { + pdsession.trigger("afterLogin", pdsession, result, errObj, xhr); + }; + + // GET RID OF progress.data.Session logout CODE (AND RELATED) IF WE DROP SUPPORT FOR USING + // THE OLD progress.data.Session API DIRECTLY (mainly a problem for existing code (tdriver), + // or anyone who wants to call methods synchronously, which should be no one) + this.logout = function (args) { + var isAsync = false, + errorObject = null, + xhr, + deferred, + params; + + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // Session: Cannot call logout() when authenticationModel is SSO. + // Please use the AuthenticationProvider object instead. + throw new Error(progress.data._getMsgText("jsdoMSG057", 'Session', 'logout()')); + } + + if (this.loginResult !== progress.data.Session.LOGIN_SUCCESS && this.authenticationModel) { + throw new Error("Attempted to call logout when there is no active session."); + } + + if (typeof(args) === 'object') { + isAsync = args.async; + if (isAsync && (typeof isAsync !== 'boolean')) { + throw new Error( progress.data._getMsgText("jsdoMSG033", + "Session", + 'logout', + 'The async argument was invalid.')); + } + /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes + deferred and jsdosession and we need to eventually attach them to the XHR we use + so that the promise created by the JSDOSession will work correctly + */ + deferred = args.deferred; + } + + xhr = new XMLHttpRequest(); + xhr.pdsession = this; + try { + /* logout when auth model is anonymous is a no-op on the server side + (but we need to set _jsdosession and _deferred anyway to make promise work + if logout was called by a JSDOSession) */ + xhr._jsdosession = jsdosession; // in case the caller is a JSDOSession + xhr._deferred = deferred; // in case the caller is a JSDOSession + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM || + this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { + if (isAsync) { + xhr.onreadystatechange = this._onReadyStateChangeGeneric; + xhr.onResponseFn = this._processLogoutResult; + xhr.onResponseProcessedFn = this._logoutComplete; + } + + + xhr.open('GET', this.serviceURI + "/static/auth/j_spring_security_logout", isAsync); + + /* instead of calling _addWithCredentialsAndAccept, we code the withCredentials + * and setRequestHeader inline so we can do it slightly differently. That + * function deliberately sets the request header inside the try so we don't + * run into a FireFox oddity that would give us a successful login and then + * a failure on getCatalog (see the comment on that function). On logout, + * however, we don't care -- just send the Accept header so we can get a 200 + * response + */ + try { + xhr.withCredentials = true; + } + catch (e) { + } + + xhr.setRequestHeader("Accept", "application/json"); + + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(this, xhr); + + if (typeof this.onOpenRequest === 'function') { + setLastSessionXHR(xhr, this); + params = { + "xhr": xhr, + "verb": "GET", + "uri": this.serviceURI + "/static/auth/j_spring_security_logout", + "async": false, + "formPreTest": false, + "session": this + }; + this.onOpenRequest(params); + xhr = params.xhr; + } + + setLastSessionXHR(xhr, this); + xhr.send(); + } + else { + xhr._anonymousLogoutOK = true; + } + } + catch (e) { + this._reinitializeAfterLogout(this, false); + throw e; + } + + if (!isAsync) { + try { + this._processLogoutResult(xhr); + } + catch (e) { + throw e; + } + } + + if (isAsync && this.authenticationModel === progress.data.Session.AUTH_TYPE_ANON) { + // fake async for Anonymous -- fire afterLogout event + try { + this._processLogoutResult(xhr); + } + catch (e) { + errorObject = e; + } + this._logoutComplete(this, null, errorObject, xhr); + } + + }; + + // This function erases all evidence of itself from the ServicesManager and + // flips a bit to prevent it to be used in the future + this.invalidate = function () { + isInvalidated = true; + cleanServicesManager(); + }; + + this._logoutComplete = function (pdsession, result, errorObject, xhr) { + // ignore result, it doesn't apply to logout -- is probably null or GENERAL_FAILURE + // we include it so onReadyStateChangeGeneric calls this correctly + pdsession.trigger("afterLogout", pdsession, errorObject, xhr); + }; + + this._processLogoutResult = function (xhr) { + var logoutSucceeded; + var pdsession = xhr.pdsession; + var basicStatusOK = false; + + if (xhr._anonymousLogoutOK) { + logoutSucceeded = true; + } + else if (xhr.status !== 200) { + /* Determine whether an error returned from the server is really an error + */ + if (pdsession.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { + /* If the Auth model is Basic, we probably got back a 404 Not found. + * But that's OK, because logout from Basic is meaningless on the + * server side unless it happens to be stateful, which is the only + * reason we even try calling j_spring_security_logout + */ + if (xhr.status === 404) { + logoutSucceeded = true; + } + else { + logoutSucceeded = false; + throw new Error("Error logging out, HTTP status = " + xhr.status); + } + } + else { + // for Form auth, any error on logout is an error + logoutSucceeded = false; + + // page refresh - we should call _reinitializeAfterLogout, or do something, so that + // caller can try logging in again (this is not a problem specific to page refresh, + // but the case of a page refresh after a server has gone down emphasizes it) + + throw new Error("Error logging out, HTTP status = " + xhr.status); + } + } + else { + logoutSucceeded = true; + } + + updateContextPropsFromResponse(pdsession, xhr); + pdsession._reinitializeAfterLogout(pdsession, logoutSucceeded); + }; + + this._reinitializeAfterLogout = function (pdsession, success) { + setLoginResult(null, pdsession); + setLoginHttpStatus(null, pdsession); + setClientContextID(null, pdsession); + setUserName(null, pdsession); + _password = null; + setAuthProvider(null); + + if (success) { + setRestApplicationIsOnline(false); + setOepingAvailable(false); + setPartialPingURI(defaultPartialPingURI); + setLastSessionXHR(null, pdsession); + clearTimeout(_timeoutID); // stop autopinging + } + }; + + + /* addCatalog + * + */ + this.addCatalog = function (arg1, arg2, arg3, arg4) { + var catalogURI, + catalogUserName, + catalogPassword, + isAsync = false, + xhr, + deferred, + iOSBasicAuthTimeout, + catalogIndex, + authProvider, + that = this; + + function addCatalogAfterOpen() { + /* This is here as much for CORS situations as the possibility that there might be an + * out of date cached version of the catalog. The CORS problem happens if you have + * accessed the catalog locally and then run an app on a different server that requests + * the catalog. Your browser already has the catalog, but the request used to get it was + * a non-CORS request and the browser will raise an error + */ + progress.data.Session._setNoCacheHeaders(xhr); + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(that, xhr); + + if (isAsync) { + xhr.onreadystatechange = that._onReadyStateChangeGeneric; + xhr.onResponseFn = that._processAddCatalogResult; + xhr.onResponseProcessedFn = that._addCatalogComplete; + + if (that.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC + && isUserAgentiOS + && iOSBasicAuthTimeout) { + xhr._requestTimeout = setTimeout(function () { + clearTimeout(xhr._requestTimeout); + xhr._iosTimeOutExpired = true; + xhr.abort(); + }, + iOSBasicAuthTimeout); + } + + // in case the caller is a JSDOSession + xhr._jsdosession = jsdosession; + xhr._deferred = deferred; + xhr._catalogIndex = catalogIndex; + } + + try { + if (typeof that.onOpenRequest === 'function') { + setLastSessionXHR(xhr, that); + var params = { + "xhr": xhr, + "verb": "GET", + "uri": catalogURI, + "async": false, + "formPreTest": false, + "session": that + }; + that.onOpenRequest(params); + xhr = params.xhr; + } + + setLastSessionXHR(xhr, that); + xhr.send(null); + } catch (e) { + throw new Error("Error retrieving catalog '" + catalogURI + "'.\n" + e.message); + } + if (isAsync) { + return progress.data.Session.ASYNC_PENDING; + } else { + return that._processAddCatalogResult(xhr); + } + + } + + if (this._isInvalidated) { + // JSDOSession: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); + } + + // Assume we're using a custom username/pw/authprovider + customCredentials = true; + + // check whether the args were passed in a single object. If so, copy them + // to the named arguments and a variable + if (arguments.length > 0) { + if (typeof arg1 === 'object') { + // check whether it's OK to add a catalog whilst offline + if (!arguments[0].offlineAddCatalog) { + if ((this.loginResult !== progress.data.Session.LOGIN_SUCCESS + && !this._authProvider) + && this.authenticationModel) { + throw new Error("Attempted to call addCatalog when there is no active session."); + } + } + + catalogURI = arg1.catalogURI; + if (!catalogURI || (typeof catalogURI !== 'string')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', + 'The catalogURI argument was missing or invalid.')); + } + catalogUserName = arg1.userName; + if (catalogUserName && (typeof catalogUserName !== 'string')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', + 'The catalogUserName argument was invalid.')); + } + catalogPassword = arg1.password; + if (catalogPassword && (typeof catalogPassword !== 'string')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', + 'The catalogPassword argument was invalid.')); + } + isAsync = arg1.async; + if (isAsync && (typeof isAsync !== 'boolean')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', + 'The async argument was invalid.')); + } + iOSBasicAuthTimeout = arg1.iOSBasicAuthTimeout; + if (typeof iOSBasicAuthTimeout === 'undefined') { + iOSBasicAuthTimeout = defaultiOSBasicAuthTimeout; + } else if (iOSBasicAuthTimeout && (typeof iOSBasicAuthTimeout !== 'number')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', + 'The iOSBasicAuthTimeout argument was invalid.')); + } + authProvider = arg1.authProvider; + + /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes + deferred, jsdosession, and catalogIndex and we need to eventually attach them to the + XHR we use so that the promise created by the JSDOSession will work correctly + */ + deferred = arg1.deferred; + catalogIndex = arg1.catalogIndex; + } else { + catalogURI = arg1; + if (typeof catalogURI !== 'string') { + throw new Error("First argument to Session.addCatalog must be the URL of the catalog."); + } + catalogUserName = arg2; + if (catalogUserName && (typeof catalogUserName !== 'string')) { + throw new Error("Second argument to Session.addCatalog must be a user name string."); + } + catalogPassword = arg3; + if (catalogPassword && (typeof catalogPassword !== 'string')) { + throw new Error("Third argument to Session.addCatalog must be a password string."); + } + } + } else { + throw new Error("Session.addCatalog is missing its first argument, the URL of the catalog."); + } + + if (!authProvider) { + authProvider = this._authProvider; + + // Guess we're using the default credentials passed earlier + customCredentials = false; + } + + // TODO: we expect that there will always be an authProvider if a login has been done. + // Therefore, we don't need to set catalogUsername and catalogPassword if they aren't + // passed in. What we should do here, when we extend the AuthenticationProvider API + // for the older auth models, is take any uname and pw passed in and create an auth + // provider, log in to the catalogURI with it, create an authImpl, and then fetch the + // catalog. + if (!catalogUserName) { + catalogUserName = this.userName; + } + + if (!catalogPassword) { + catalogPassword = _password; + } + + xhr = new XMLHttpRequest(); + xhr.pdsession = this; + xhr._catalogURI = catalogURI; + + // for now we don't support multiple version of the catalog across sessions + if (progress.data.ServicesManager.getSession(catalogURI) !== undefined) { + if (isAsync) { + /* + Attempt to get the event to fire AFTER this call returns ASYNC_PENDING + (and if the method was called from a JSDOSession, create an xhr to communicate + information related to promises back to its afterAddCatalog handler). Note that + the xhr is never used to make a request, it just carries data in the way + expected by the handler) + */ + // in case the caller is a JSDOSession + xhr._jsdosession = jsdosession; + xhr._deferred = deferred; + xhr._catalogIndex = catalogIndex; + + setTimeout(this._addCatalogComplete, 10, this, + progress.data.Session.CATALOG_ALREADY_LOADED, null, xhr); + return progress.data.Session.ASYNC_PENDING; + } + return progress.data.Session.CATALOG_ALREADY_LOADED; + } + + if (authProvider) { + authProvider._openRequestAndAuthorize(xhr, 'GET', catalogURI, isAsync, addCatalogAfterOpen); + // existing code in JSDOSession addCatalog expects to get this as a return value, + // have to return it now + return progress.data.Session.ASYNC_PENDING; + } else { // should be able to get rid of this if we do away with synchronous (old Session API) support + this._setXHRCredentials(xhr, 'GET', catalogURI, catalogUserName, catalogPassword, isAsync); + // Note that we are not adding the CCID to the URL or as a header, because the catalog may not + // be stored with the REST app and even if it is, the AppServer ID shouldn't be relevant + + return addCatalogAfterOpen(); + } + + }; + + this._processAddCatalogResult = function (xhr) { + var _catalogHttpStatus = xhr.status; + var theSession = xhr.pdsession; + var servicedata; + var catalogURI = xhr._catalogURI, + serviceURL, + theJSDOSession = jsdosession; + + // Only change the Session's state if the default AuthProv is being used + if (!customCredentials) { + toggleOnlineState(xhr); + } + + if ((_catalogHttpStatus == 200) || (_catalogHttpStatus === 0) && xhr.responseText) { + servicedata = theSession._parseCatalog(xhr); + try { + progress.data.ServicesManager.addCatalog(servicedata, theSession); + } + catch (e) { + if (progress.data.ServicesManager.getSession(catalogURI) !== undefined) { + /* this failed because the catalog had already been loaded, but the code + in addCatalog did not catch that, probably because we are executing + the JSDOSession addCatalog with multiple catalogURIs passed, and 2 + are the same + */ + return progress.data.Session.CATALOG_ALREADY_LOADED; + } + // different catalogs, with same resource name + throw new Error("Error processing catalog '" + catalogURI + "'. \n" + e.message); + } + // create a mobile service object and add it to the Session's array of same + for (var i = 0; i < servicedata.length; i++) { + serviceURL = theSession._prependAppURL(servicedata[i].address); + pushService(new progress.data.MobileServiceObject( + { + name: servicedata[i].name, + uri: serviceURL + }), + theSession); + + if (servicedata[i].settings + && servicedata[i].settings.useXClientProps + && !theSession.xClientProps) { + console.warn("Catalog warning: Service settings property 'useXClientProps' " + + "is true but 'xClientProps' property has not been set."); + } + } + pushCatalogURIs(catalogURI, theSession); + progress.data.ServicesManager.addSession(catalogURI, theSession); + if (theJSDOSession) { + progress.data.ServicesManager.addJSDOSession(catalogURI, theJSDOSession); + } + } + else if (_catalogHttpStatus == 401) { + return progress.data.AuthenticationProvider._getAuthFailureReason(xhr); + } + else if (xhr._iosTimeOutExpired) { + throw new Error( progress.data._getMsgText("jsdoMSG047", "addCatalog") ); + } + else { + throw new Error("Error retrieving catalog '" + catalogURI + + "'. Http status: " + _catalogHttpStatus + "."); + } + + return progress.data.Session.SUCCESS; + }; + + this._addCatalogComplete = function (pdsession, result, errObj, xhr) { + pdsession.trigger("afterAddCatalog", pdsession, result, errObj, xhr); + }; + + + /* + * ping -- determine whether the Mobile Web Application that the Session object represents + * is available, which includes determining whether its associated AppServer is running + * Also determine whether the Mobile services managed by this Session object are available + * (which means simply that they're known to the Mobile Web Application) + * (Implementation note: be sure that this Session object's "connected" + * property retains its current value until the end of this function, where + * it gets updated, if necessary, after calling _isOnlineStateChange + * + * Signatures : + * @param arg + * There are 2 signatures -- + * - no argument -- do an async ping of the Session's Mobile Web application. The only effect + * of the ping will be firing an offline or an online event, if appropriate + * The ping function itself will return false to the caller + * - object argument -- the object's properties provide the input args. They are all + * optional (if for some reason the caller passes an object that has no properties, it's + * the same as passing no argument at all). The properties may be: + * async -- tells whether to execute the ping asynchronously (which is the default) + * onCompleteFn -- if async, this will be called when response returns + * doNotFireEvent -- used internally, controls whether the ping method causes an offline + * or online event to be fired if there has been a change (the default is that it + * does, but our Session._checkServiceResponse() sets this to true so that it can + * control the firing of the event) + * offlineReason -- if present, and if the ping code discovers that teh server is offline, + * the ping code will set this with its best guess + * as to the reason the server is offline + */ + this.ping = function (args) { + var pingResult = false; + var pingArgs = { + pingURI: null, async: true, onCompleteFn: null, + fireEventIfOfflineChange: true, onReadyStateFn: this._onReadyStateChangePing, + offlineReason: null + }; + + if (this._isInvalidated) { + // Session: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "Session")); + } + + if ((!this._authProvider) && (this.loginResult !== progress.data.Session.LOGIN_SUCCESS)) { + throw new Error("Attempted to call ping when not logged in."); + } + + if (args) { + if (args.async !== undefined) { + // when we do background pinging (because pingInterval is set), + // we pass in an arg that is just an object that has an async property, + // set to true. This can be expanded to enable other kinds of ping calls + // to be done async (so that application developers can do so, if we decide + // to support that) + pingArgs.async = args.async; + } + + if (args.doNotFireEvent !== undefined) { + pingArgs.fireEventIfOfflineChange = !args.doNotFireEvent; + } + + if (args.onCompleteFn && (typeof args.onCompleteFn) == 'function') { + pingArgs.onCompleteFn = args.onCompleteFn; + } + /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes + deferred and jsdosession and we need to eventually attach them to the XHR we use so that + the promise created by the JSDOSession will work correctly + */ + pingArgs.deferred = args.deferred; + pingArgs.jsdosession = args.jsdosession; + + } + + + /* Ping the Mobile Web Application (this will also determine whether AppServer is available) + * Call _processPingResult() if we're synchronous, otherwise the handler for the xhr.send() + * will call it + */ + pingArgs.pingURI = that._makePingURI(); + that._sendPing(pingArgs); + if (!pingArgs.async) { + if (pingArgs.xhr) { + pingResult = that._processPingResult(pingArgs); + if (args.offlineReason !== undefined) { + args.offlineReason = pingArgs.offlineReason; + } + } + else { + pingResult = false; // no xhr returned from _sendPing, something must have gone wrong + } + if ( args.xhr !== undefined ) { + // if it's a sync ping, return the xhr if caller indicates they want it + // (there's almost guaranteed to be one, even if the ping was never sent + // if for some reason there isn't, we give them the null or undefined we ended up with) + args.xhr = pingArgs.xhr; + } + } + // else it's async, deliberately returning false + // so developer not misled into thinking the ping succeeded + + return pingResult; + }; + + + // "protected" Functions + + /* + * given a value of true or false for being online for the Mobile Web Application + * managed by this Session object, determine whether that changes the current + * state of being offline or online. + * Returns true if the input state is a change from the current state + * + * Signature : + * @param isOnline Required. True to determine whether online is a state change, false to + * determine whether offline constitutes a state change. Boolean. + * + */ + this._isOnlineStateChange = function (isOnline) { + var stateChanged = false; + + if (isOnline && !(this.connected)) { + stateChanged = true; + } + else if (!isOnline && ( this.connected )) { + stateChanged = true; + } + + return stateChanged; + }; + + + /* + * given information about the response from a request made to a service, + * do the following: + * + * determine whether the online status of the Session has changed, and + * set the Session's Connected property accordingly + * if the Session's online status has changed, fire the appropriate event + * + * Signature : + * @param xhr Required. The xhr that was used to make the request. Object + * @param success Required. True if caller regards the request as having succeeded. Boolean + * @param request Required. The JSDO request object created for making the request. Object. + * + */ + this._checkServiceResponse = function (xhr, success, request) { + var offlineReason = null, + wasOnline = this.connected; + updateContextPropsFromResponse(this, xhr); + + /* first of all, if there are no subscriptions to offline or online events, don't + * bother -- we don't want to run the risk of messing things up by calling ping + * if the app developer isn't interested (especially because that may mean that + * ping isn't enabled on the server, anyway) + */ + if (!this._events) { + return; + } + var offlineObservers = this._events["offline"] || []; + var onlineObservers = this._events["online"] || []; + if ((offlineObservers.length === 0) && (onlineObservers.length === 0)) { + return; + } + + /* even though this function gets called as a result of trying to + * contact the server, don't bother to change anything if we already + * know that the device (or user agent, or client machine) is offline. + * We can't assume anything about the state of the server if we can't + * even get to the internet from the client + */ + + // if the call to the server was a success, we will assume we are online, + // both server and device + if (success) { + setRestApplicationIsOnline(true); + setDeviceIsOnline(true); // presumably this is true (probably was already true) + } + else { + /* Request failed, determine whether it's because server is offline + * Do this even if the Session was already in an offline state, because + * we need to determine whether the failure was due to still being + * offline, or whether it's now possible to communicate with the + * server but the problem was something else. + */ + + if (deviceIsOnline) { + /* ping the server to get better information on whether this is an offline case + * NB: synchronous ping for simplicity, maybe should consider async so as not + * to potentially freeze UI + */ + var localPingArgs = { + doNotFireEvent: true, // do in this fn so we have the request + offlineReason: null, + async: false + }; + if (!(that.ping(localPingArgs) )) { + offlineReason = localPingArgs.offlineReason; + setRestApplicationIsOnline(false); + } + else { + // ping returned true, so even though the original request failed, + // we are online and the failure must have been due to something else + setRestApplicationIsOnline(true); + } + } + // else deviceIsOnline was already false, so the offline event should already have + // been fired for that reason and there is no need to do anything else + } + + if (wasOnline && !this.connected) { + this.trigger("offline", this, offlineReason, request); + } + else if (!wasOnline && this.connected) { + this.trigger("online", this, request); + } + }; + + /* Decide whether, on the basis of information returned by a server request, the + * Mobile Web Application managed by this Session object is online, where online + * means that the ping response was a 200 and, IF the body of the response contains + * JSON with an AppServerStatus property, that AppServerStatus Status property has + * a pingStatus property set to true + * i.e., the body has an AppServerStatus.PingStatus set to true + * (if the body doesn't contain JSON with an AppServerStatus, we use just the HTTP + * response status code to decide) + * + * Returns: true if the response meets the above conditions, false if it doesn't + * + * Parameters: + * args, with properties: + * xhr - the XMLHttpRequest used to make the request + * offlineReason - if the function determines that the app is offline, + * it sets offlineReason to the reason for that decision, + * for the use of the caller + * fireEventIfOfflineChange - if true, the function fires an offline or online + * event if there has been a change (i.e., the online state determined + * by the function is different from what it had been when the function + * began executing) + * usingOepingFormat - OPTIONAL. The function's default assumption is that the value + * of the session's internal oepingAvailable variable indicates whether the + * the response body will be in the format used by the OpenEdge oeping service. + * A caller can override this assumption by using this property to true or false. + * (the isAuthorized code sets this to false because it doesn't use oeping + * but does call this function) + */ + this._processPingResult = function (args) { + var xhr = args.xhr, + pingResponseJSON, + appServerStatus = null, + wasOnline = this.connected, + connectedBeforeCallback, + assumeOepingFormat; + + if (args.hasOwnProperty('usingOepingFormat')) { + assumeOepingFormat = args.usingOepingFormat; + } else { + assumeOepingFormat = oepingAvailable; + } + + /* first determine whether the Web server and the Mobile Web Application (MWA) + * are available + */ + if (xhr.status >= 200 && xhr.status < 300) { + updateContextPropsFromResponse(this, xhr); + if (assumeOepingFormat) { + try { + pingResponseJSON = JSON.parse(xhr.responseText); + appServerStatus = pingResponseJSON.AppServerStatus; + } + catch (e) { + /* We got a successful response from calling our ping URI, but it + * didn't return valid JSON. If we think that the oeping REST API + * is available on the server (so we should have gotten valid + * json), log this to the console. + * + */ + console.error("Unable to parse ping response."); + } + } + toggleOnlineState(xhr); + } + else { + if (deviceIsOnline) { + if (xhr.status === 0) { + args.offlineReason = progress.data.Session.SERVER_OFFLINE; + setRestApplicationIsOnline(false); + } + else if ((xhr.status === 404) || (xhr.status === 410)) { + /* if we get a 404, it means the Web server is up, but it + * can't find the resource we requested (either _oeping or + * the login target), therefore the Mobile Web application + * must be unavailable (410 is Gone) + */ + args.offlineReason = progress.data.Session.WEB_APPLICATION_OFFLINE; + setRestApplicationIsOnline(false); + } + else { + /* There's some error, but we can't say for sure that it's because + * the Web application is unavailable. May be an authentication problem, + * internal server error, or for some reason our ping request was + * invalid (unlikely to happen if it previously succeeded). + * In particular, if the server uses Form authentication, it + * may have come back online but now the session id + * is no longer valid. + */ + setRestApplicationIsOnline(true); + } + } + else { + args.offlineReason = progress.data.Session.DEVICE_OFFLINE; + } + } + + // is the AppServer online? appServerStatus will be non-null only + // if the ping request returned 200, meaning the other things are OK + // (connection to server, Tomcat, Mobile Web application) + if (appServerStatus) { + if (appServerStatus.PingStatus === "false") { + args.offlineReason = progress.data.Session.APPSERVER_OFFLINE; + setRestApplicationIsOnline(false); + } + else { + setRestApplicationIsOnline(true); + } + } + + /* We call any async ping callback handler and then, after that returns, fire an + offline or online event if necessary. + When deciding whether to fire an event, the responsibility of this _processPingResult() + function is to decide about the event on the basis of the data returned from the ping + that it is currently processing. Therefore, since the ping callback that is just about + to be called could change the outcome of the event decision (for example, if the handler + calls logout(), thus setting Session.connected to false)), we save the current value of + Session.connected and use that saved value to decide about the event after the ping + handler returns. + (If the application programmer wants to get an event fired as a result of something + that happens in the ping handler, they should call a ping() *after* that. + */ + connectedBeforeCallback = this.connected; + + if ((typeof xhr.onCompleteFn) == 'function') { + xhr.onCompleteFn({ + pingResult: this.connected, + xhr: xhr, + offlineReason: args.offlineReason + }); + } + + // decide whether to fire an event, and if so do it + if (args.fireEventIfOfflineChange) { + if (wasOnline && !connectedBeforeCallback) { + that.trigger("offline", that, args.offlineReason, null); + } + else if (!wasOnline && connectedBeforeCallback) { + that.trigger("online", that, null); + } + } + + return this.connected; + }; + + + this._onReadyStateChangePing = function () { + var xhr = this; + var args; + + if (xhr.readyState == 4) { + args = { + xhr: xhr, + fireEventIfOfflineChange: true, + offlineReason: null + }; + that._processPingResult(args); + if (_pingInterval > 0) { + _timeoutID = setTimeout(that._autoping, _pingInterval); + } + } + }; + + this._pingtestOnReadyStateChange = function () { + var xhr = this; + + if (xhr.readyState == 4) { + var foundOeping = false; + if (xhr.status >= 200 && xhr.status < 300) { + foundOeping = true; + } + else { + setPartialPingURI(that.loginTarget); + console.warn("Default ping target not available, will use loginTarget instead."); + } + setOepingAvailable(foundOeping); + + // If we're here, we've just logged in. If pingInterval has been set, we need + // to start autopinging + if (_pingInterval > 0) { + _timeoutID = setTimeout(that._autoping, _pingInterval); + } + } + }; + + /* + * args: pingURI + * async + * onCompleteFn used only if async is true + * + * (deliberately not catching thrown error) + */ + this._sendPing = function (args) { + var xhr = new XMLHttpRequest(), + that = this; + + function sendPingAfterOpen() { + if (args.async) { + xhr.onreadystatechange = args.onReadyStateFn; + xhr.onCompleteFn = args.onCompleteFn; + xhr._jsdosession = jsdosession; // in case the Session is part of a JSDOSession + xhr._deferred = args.deferred; // in case the Session is part of a JSDOSession + } + progress.data.Session._setNoCacheHeaders(xhr); + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(that, xhr); + if (that.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + _addWithCredentialsAndAccept(xhr, + "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + } + xhr.send(null); + } + + try { + if (this._authProvider) { + this._authProvider._openRequestAndAuthorize(xhr, + 'GET', + args.pingURI, + args.async, + sendPingAfterOpen); + } else { + // get rid of this if we do away with synchronous support (i.e., customer use of + // old Session API) + this._setXHRCredentials(xhr, "GET", args.pingURI, this.userName, _password, args.async); + + // Sending the XHR request after opening the channel + if (xhr.readyState === 1) { + sendPingAfterOpen(); + } + } + } catch (e) { + args.error = e; + } + + args.xhr = xhr; + }; + + this._makePingURI = function () { + var pingURI = this.serviceURI + partialPingURI; + // had caching problem with Firefox in its offline mode + if (progress.data.Session._useTimeStamp) { + pingURI = progress.data.Session._addTimeStampToURL(pingURI); + } + return pingURI; + }; + + + /* + * autoping -- callback + */ + this._autoping = function () { + that.ping({async: true}); + }; + + + // TODO for API revamp: get rid of this method and replace it with implementations + // of AUthenticationImplementation.openRequest that are specific to the + // auth models (assuming we can use some sort of subclassing or interface design) + // (and when we remove this, remove the calls to it in this file) + /* _setXHRCredentials (intended for progress.data library use only) + * set credentials as needed, both via the xhr's open method and setting the + * Authorization header directly + */ + this._setXHRCredentials = function (xhr, verb, uri, userName, password, async) { + + // note that we do not set credentials if userName is null. + // Null userName indicates that the developer is depending on the browser to + // get and manage the credentials, and we need to make sure we don't interfere with that + if (userName + && this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { + + // See the comment at the definition of the canPassCredentialsToOpen() function + // for why we pass credentials to open() in some cases but not others. (If we're not using + // Basic auth, we never pass credentials) + if (canPassCredentialsToOpen()) { + xhr.open(verb, uri, async, userName, password); + } + else { + xhr.open(verb, uri, async); + } + + // set Authorization header + var auth = _make_basic_auth(userName, password); + xhr.setRequestHeader('Authorization', auth); + } + else { + xhr.open(verb, uri, async); + } + }; + + /* _addCCIDtoURL (intended for progress.data library use only) + * Add the Client Context ID being used by a session on an OE REST application, if we have + * previously stored one from a response from the server + */ + this._addCCIDtoURL = function (url) { + var urlPart1, + urlPart2, + jsessionidStr, + index; + + if (this.clientContextId && (this.clientContextId !== "0")) { + // Should we test protocol, + // host and port in addition to path to ensure that jsessionid is only sent + // when request applies to the REST app (it might not be if the catalog is somewhere else) + if (url.substring(0, this.serviceURI.length) == this.serviceURI) { + jsessionidStr = ";" + "JSESSIONID=" + this.clientContextId; + index = url.indexOf('?'); + if (index == -1) { + url += jsessionidStr; // just append the jsessionid path parameter to the path + } + else { + // insert jsessionid path parameter before the first query parameter + urlPart1 = url.substring(0, index); + urlPart2 = url.substring(index); + url = urlPart1 + jsessionidStr + urlPart2; + } + } + } + return url; + }; + + /* _saveClientContextId (intended for progress.data library use only) + * If the CCID hasn't been set for the session yet, check the xhr for it and store it. + * (If it has been set, assume that the existing one is correct and do nothing. We could + * enhance this function by checking to see whether the new one matches the existing one. + * Not sure what to do if that's the case -- overwrite the old one? ignore the new one? + * Should at least log a warning or error + */ + this._saveClientContextId = function (xhr) { + // do this unconditionally (even if there is already a client-context-id), because + // if basic authentication is set up such that it uses sessions, and cookies are disabled, + // the server will generate a different session on each request and the X-CLIENT-CONTEXT-ID + // will therefore be different + setClientContextIDfromXHR(xhr, this); + }; + + this._parseCatalog = function (xhr) { + var jsonObject; + var catalogdata; + + try { + jsonObject = JSON.parse(xhr.responseText); + catalogdata = jsonObject.services; + } + catch (e) { + console.error("Unable to parse response. Make sure catalog has correct format."); + catalogdata = null; + } + + return catalogdata; + }; + + /* _prependAppURL + * Prepends the URL of the Web application + * (the 1st parameter passed to login, stored in this.serviceURI) + * to whatever string is passed in. If the string passed in is an absolute URL, this function does + * nothing except return a copy. This function ensures that the resulting URL has the correct number + * of slashes between the web app url and the string passed in (currently that means that if what's + * passed in has no initial slash, the function adds one) + */ + this._prependAppURL = function (oldURL) { + if (!oldURL) { + /* If oldURL is null, just return the app URL. (It's not the responsibility of this + * function to decide whether having a null URL is an error. Its only responsibility + * is to prepend the App URL to whatever it gets passed + * (and make sure the result is a valid URL) + */ + return this.serviceURI; + } + var newURL = oldURL; + var pat = /^https?:\/\//i; + if (!pat.test(newURL)) { + if (newURL.indexOf("/") !== 0) { + newURL = "/" + newURL; + } + + newURL = this.serviceURI + newURL; + } + return newURL; + }; + + + // Functions + + // get rid of this if we get rid of synchronous (old Session object API) support? + // Set an XMLHttpRequest object's withCredentials attribute and Accept header, + // using a try-catch so that if setting withCredentials throws an error it doesn't + // interrupt execution (this is a workaround for the fact that Firefox doesn't + // allow you to set withCredentials when you're doing a synchronous operation) + // The setting of the Accept header is included here, and happens after the + // attempt to set withCredentials, to make the behavior in 11.3.0 match + // the behavior in 11.2.1 -- for Firefox, in a CORS situation, login() will + // fail. (If we allowed the Accept header to be set, login() would succeed + // because of that but addCatalog() would fail because no JSESSIONID would + // be sent due to withCredentials not being true) + function _addWithCredentialsAndAccept(xhr, acceptString) { + try { + xhr.withCredentials = true; + xhr.setRequestHeader("Accept", acceptString); + } + catch (e) { + } + } + + // get rid of this if we get rid of synchronous (old Session API) support? + // (because it's in AuthenticationProviderBasic) + // from http://coderseye.com/2007/how-to-do-http-basic-auth-in-ajax.html + function _make_basic_auth(user, pw) { + var tok = user + ':' + pw; + var hash = btoa(tok); + return "Basic " + hash; + } + + /* The next 2 functions, _gotLoginForm() and _gotLoginFailure(), attempt to determine whether + * a server response consists of + * the application's login page or login failure page. Currently (release 11.2), this + * is the only way we have of determining that a request made to the server that's + * configured for form-based authentication failed due to authentication (i.e., + * authentication hadn't happened before the request and either invalid credentials or + * no credentials were sent to the server). That's because, due to the fact that the browser + * or native wrapper typically intercepts the redirect involved in an unauthenticated request + * to a server that's using using form auth, all we see in the XHR is a success status code + * plus whatever page we were redirected to. + * In the future, we expect to enhance the OE REST adapter so that it will return a status code + * indicating failure for form-based authentication, and we can reimplement these functions so + * they check for that code rather than do the simplistic string search. + */ + + // Determines whether the content of the xhr is the login page. Assumes + // use of a convention for testing for login page + var loginFormIDString = "j_spring_security_check"; + + function _gotLoginForm(xhr) { + // is the response contained in an xhr actually the login page? + return _findStringInResponseHTML(xhr, loginFormIDString); + } + + // Determines whether the content of the xhr is the login failure page. Assumes + // use of a convention for testing for login fail page + var loginFailureIdentificationString = "login failed"; + + function _gotLoginFailure(xhr) { + return _findStringInResponseHTML(xhr, loginFailureIdentificationString); + } + + // Does a given xhr contain html and does that html contain a given string? + function _findStringInResponseHTML(xhr, searchString) { + if (!xhr.responseText) { + return false; + } + var contentType = xhr.getResponseHeader("Content-Type"); + + if ((contentType.indexOf("text/html") >= 0) && + (xhr.responseText.indexOf(searchString) >= 0)) { + return true; + } + + return false; + } + + // get rid of this if we get rid of synchronous (old Session API) support? + /* sets the statusFromjson property in the params object to indicate + * the status of a response from an OE Mobile Web application that has + * to do with authentication (the response to a login request, or a + * response to a request for a resource where there was an error having + * to do with authentication */ + function handleJSONLoginResponse(params) { + // Parse the json in the response to see whether it's the special OE REST service + // response. If it is, check the result (which should be consistent with the status from + // the xhr) + var jsonObject; + params.statusFromjson = null; + try { + jsonObject = JSON.parse(params.xhr.responseText); + + if (jsonObject.status_code !== undefined + && jsonObject.status_txt !== undefined) { + params.statusFromjson = jsonObject.status_code; + } + } + catch (e) { + // invalid json + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, params.session); + setLoginHttpStatus(xhr.status, params.session); + throw new Error("Unable to parse login response from server."); + } + + } + + function setRequestHeaderFromContextProps(session, xhr) { + if (session.xClientProps) { + xhr.setRequestHeader("X-CLIENT-PROPS", session.xClientProps); + } + else if (session._contextProperties.contextHeader !== undefined) { + xhr.setRequestHeader("X-CLIENT-PROPS", session._contextProperties.contextHeader); + } + } + + function toggleOnlineState(xhr) { + var pdsession = that; + + setLoginHttpStatus(xhr.status, pdsession); + + if (pdsession.loginHttpStatus >= 200 && pdsession.loginHttpStatus < 400) { + setLoginResult(progress.data.Session.LOGIN_SUCCESS, pdsession); + setRestApplicationIsOnline(true); + pdsession._saveClientContextId(xhr); + storeAllSessionInfo(); // save info to persistent storage + } else { + // Taking a page from _processPingResult where we set the rest application as offline if it's one of + // these error codes + if (pdsession.loginHttpStatus === 0 || pdsession.loginHttpStatus === 400 || pdsession.loginHttpStatus === 410) { + setRestApplicationIsOnline(false); + setLoginResult(progress.data.AuthenticationProvider._getAuthFailureReason(xhr), + pdsession); + } + // Otherwise if it's probably an internal error or auth problem. Either way, we know it's still online. + else { + + setRestApplicationIsOnline(true); + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, pdsession); + } + + + } + + setLastSessionXHR(xhr, pdsession); + updateContextPropsFromResponse(pdsession, xhr); + + return pdsession.loginResult; + }; + + function updateContextPropsFromResponse(session, xhr) { + /* determine whether the response contains an X-CLIENT_PROPS header and, if so, + set the Session's context + */ + var contextString, + context; + + if (xhr) { + contextString = getResponseHeaderNoError(xhr, "X-CLIENT-PROPS"); + if (contextString) { + try { + context = JSON.parse( contextString ); + } + catch(e) { + } + if (typeof context === "object") { + session._contextProperties.setContext( context ); + } + else { + //{1}: A server response included an invalid {2} header. + throw new Error(progress.data._getMsgText("jsdoMSG123", 'Session', 'X-CLIENT-PROPS')); + } + } + else if (contextString === "") { + // If header is "", clear the X-CLIENT-PROPS context, + session._contextProperties.setContext( {} ); + } + // if header is absent (getResponseHeader will return null), don't change _contextProperties + } + } + + // Remove all resources, services, and sessions related to this Session from the ServicesManager + function cleanServicesManager() { + progress.data.ServicesManager.cleanSession(that); + } + + // process constructor options and do other initialization + + // If a storage key (name property of a JSDOSession) was passed to the constructor, + // use it to try to retrieve state data from a previous JSDOSession instance that + // had the same name. This code was introduced to handle page refreshes, but could + // be used for other purposes. + if (typeof (options) === 'object') { + + jsdosession = options.jsdosession; + newURI = options.serviceURI; + setAuthProvider(options.authProvider); // do this BEFORE calling setSessionInfoFromStorage + + if (options.authProvider && options.authProvider.hasClientCredentials()) { + _loginResult = progress.data.Session.LOGIN_SUCCESS; + } + + // get rid of trailing '/' because appending service url that starts with '/' + // will cause request failures + if (newURI && newURI[newURI.length - 1] === "/") { + newURI = newURI.substring(0, newURI.length - 1); + } + + _storageKey = options._storageKey; + if (_storageKey) { + if (retrieveSessionInfo(_storageKey)) { + storedAuthModel = retrieveSessionInfo("authenticationModel"); + storedURI = retrieveSessionInfo("serviceURI"); + + if ((storedAuthModel !== options.authenticationModel) || + (storedURI !== newURI)) { + clearAllSessionInfo(); + } else { + // Note: be sure we have set authProvider (if any) from options before + // calling setSessionInfoFromStorage (important so that the logic in + // setSessionInfoFromStorage that re-creates an AuthenticationProvider + // after page refresh only gets used if the app is using the old JSDOSession.login) + setSessionInfoFromStorage(_storageKey); + stateWasReadFromStorage = true; + } + } + // _storageKey is in essence the flag for page refresh; we are not supporting page refresh for Basic + // auth, so clear it even if it was passed in. + // (But had to set and keep _storageKey until this point so that the above validation of + // serviceURI and auth model will be done even in the case where there's a mismatch and + // the new auth model is Basic. This statement will go away when we support page refresh with + // Basic) + if (options.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { + _storageKey = undefined; + } + } + + // If we didn't read state info from storage, we need to set the serviceURI and probably + // the authenticationModel + if (!stateWasReadFromStorage) { + if (newURI) { + setServiceURI(newURI, this); + } + if (options.authenticationModel) { + this.authenticationModel = options.authenticationModel; + } + } + } + + }; // End of Session + progress.data.Session._useTimeStamp = true; + + var SEQ_MAX_VALUE = 999999999999999; + // 15 - 9 + var _tsseq = SEQ_MAX_VALUE; + // Initialized to SEQ_MAX_VALUE to initialize values. + var _tsprefix1 = 0; + var _tsprefix2 = 0; + + // this._getNextTimeStamp = function () { + progress.data.Session._getNextTimeStamp = function () { + var seq = ++_tsseq; + if (seq >= SEQ_MAX_VALUE) { + _tsseq = seq = 1; + var t = Math.floor(( Date.now ? Date.now() : (new Date().getTime())) / 10000); + if (_tsprefix1 == t) { + _tsprefix2++; + if (_tsprefix2 >= SEQ_MAX_VALUE) { + _tsprefix2 = 1; + } + } + else { + _tsprefix1 = t; + Math.random(); // Ignore call to random + _tsprefix2 = Math.round(Math.random() * 10000000000); + } + } + + return _tsprefix1 + "-" + _tsprefix2 + "-" + seq; + }; + + /* + * _addTimeStampToURL (intended for progress.data library use only) + * Add a time stamp to the a URL to prevent caching of the request. + * Set progress.data.Session._useTimeStamp = false to turn off. + */ + progress.data.Session._addTimeStampToURL = function (url) { + var timeStamp = "_ts=" + progress.data.Session._getNextTimeStamp(); + url += ((url.indexOf('?') == -1) ? "?" : "&") + timeStamp; + return url; + }; + + // Do whatever it takes to direct the XMLHttpRequest not to fulfill the request + // from a cache + // (convenience method --- we do this several different places in the code) + progress.data.Session._setNoCacheHeaders = function (xhr) { + xhr.setRequestHeader("Cache-Control", "no-cache"); + xhr.setRequestHeader("Pragma", "no-cache"); + }; + + + +// Constants for progress.data.Session + if ((typeof Object.defineProperty) == 'function') { + Object.defineProperty(progress.data.Session, 'LOGIN_AUTHENTICATION_REQUIRED', { + value: 0, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'LOGIN_SUCCESS', { + value: 1, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'LOGIN_AUTHENTICATION_FAILURE', { + value: 2, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'LOGIN_GENERAL_FAILURE', { + value: 3, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'CATALOG_ALREADY_LOADED', { + value: 4, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'ASYNC_PENDING', { + value: 5, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'EXPIRED_TOKEN', { + value: 6, enumerable: true + }); + + Object.defineProperty(progress.data.Session, 'SUCCESS', { + value: 1, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'AUTHENTICATION_FAILURE', { + value: 2, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'GENERAL_FAILURE', { + value: 3, enumerable: true + }); + + Object.defineProperty(progress.data.Session, 'AUTH_TYPE_ANON', { + value: "anonymous", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'AUTH_TYPE_BASIC', { + value: "basic", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'AUTH_TYPE_FORM', { + value: "form", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'AUTH_TYPE_SSO', { + value: "sso", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'AUTH_TYPE_FORM_SSO', { + value: "form_sso", enumerable: true + }); + + + Object.defineProperty(progress.data.Session, 'DEVICE_OFFLINE', { + value: "Device is offline", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'SERVER_OFFLINE', { + value: "Cannot contact server", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'WEB_APPLICATION_OFFLINE', { + value: "Mobile Web Application is not available", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'SERVICE_OFFLINE', { + value: "REST web Service is not available", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'APPSERVER_OFFLINE', { + value: "AppServer is not available", enumerable: true + }); + } + else { + progress.data.Session.LOGIN_SUCCESS = 1; + progress.data.Session.LOGIN_AUTHENTICATION_FAILURE = 2; + progress.data.Session.LOGIN_GENERAL_FAILURE = 3; + progress.data.Session.CATALOG_ALREADY_LOADED = 4; + + progress.data.Session.SUCCESS = 1; + progress.data.Session.AUTHENTICATION_FAILURE = 2; + progress.data.Session.GENERAL_FAILURE = 3; + + progress.data.Session.AUTH_TYPE_ANON = "anonymous"; + progress.data.Session.AUTH_TYPE_BASIC = "basic"; + progress.data.Session.AUTH_TYPE_FORM = "form"; + progress.data.Session.AUTH_TYPE_SSO = "sso"; + + /* deliberately not including the "offline reasons" that are defined in the + * 1st part of the conditional. We believe that we can be used only in environments where + * ECMAScript 5 is supported, so let's put that assumption to the test + */ + } + +//setup inheritance for Session -- specifically for incorporating an Observable object + progress.data.Session.prototype = new progress.util.Observable(); + progress.data.Session.prototype.constructor = progress.data.Session; + function validateSessionSubscribe(args, evt, listenerData) { + listenerData.operation = undefined; + var found = false; + + // make sure this event is one that we support + for (var i = 0; i < this._eventNames.length; i++) { + if (evt === this._eventNames[i].toLowerCase()) { + found = true; + break; + } + } + if (!found) { + throw new Error(progress.data._getMsgText("jsdoMSG042", evt)); + } + + if (args.length < 2) { + throw new Error(progress.data._getMsgText("jsdoMSG038", 2)); + } + + if (typeof args[0] !== 'string') { + throw new Error(progress.data._getMsgText("jsdoMSG039")); + } + + if (typeof args[1] !== 'function') { + throw new Error(progress.data._getMsgText("jsdoMSG040")); + } + else { + listenerData.fn = args[1]; + } + + if (args.length > 2) { + if (typeof args[2] !== 'object') { + throw new Error(progress.data._getMsgText("jsdoMSG041", evt)); + } + else { + listenerData.scope = args[2]; + } + } + } + // events supported by Session + progress.data.Session.prototype._eventNames = + ["offline", "online", "afterLogin", "afterAddCatalog", "afterLogout", "afterDisconnect"]; + // callback to validate subscribe and unsubscribe + progress.data.Session.prototype.validateSubscribe = validateSessionSubscribe; + progress.data.Session.prototype.toString = function (radix) { + return "progress.data.Session"; + }; + + + /* + progress.data.JSDOSession + Like progress.data.Session, but the methods are async-only and return promises. + (first implementation uses progress.data.Session to do the work, but conceivably + that implementation could be changed to something different) + The JSDOSession object keeps the same underlying pdsession object for the lifetime + of the JSDOSession object -- i.e., even after logout and subsequent login, the pdsession + is re-used rather than re-created. + */ + progress.data.JSDOSession = function JSDOSession(options) { + var _pdsession, + _serviceURI, + that = this, + _name; + + // PROPERTIES + // Approach: Use the properties of the underlying progress.data.Session object whenever + // possible. + Object.defineProperty(this, 'authenticationModel', + { + get: function () { + return _pdsession ? _pdsession.authenticationModel : undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'authProvider', + { + get: function () { + return _pdsession ? _pdsession._authProvider : null; + }, + enumerable: true + }); + Object.defineProperty(this, 'catalogURIs', + { + get: function () { + return _pdsession ? _pdsession.catalogURIs: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'clientContextId', + { + get: function () { + return _pdsession ? _pdsession.clientContextId: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'connected', + { + get: function () { + return _pdsession ? _pdsession.connected: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'JSDOs', + { + get: function () { + return _pdsession ? _pdsession.JSDOs: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'loginResult', + { + get: function () { + return _pdsession ? _pdsession.loginResult: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'loginHttpStatus', + { + get: function () { + return _pdsession ? _pdsession.loginHttpStatus: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'onOpenRequest', + { + get: function () { + return _pdsession ? _pdsession.onOpenRequest: undefined; + }, + set: function (newval) { + if (_pdsession) { + _pdsession.onOpenRequest = newval; + } + }, + enumerable: true + }); + + Object.defineProperty(this, 'pingInterval', + { + get: function () { + return _pdsession ? _pdsession.pingInterval: undefined; + }, + set: function (newval) { + if (_pdsession) { + _pdsession.pingInterval = newval; + } + }, + enumerable: true + }); + + Object.defineProperty(this, 'services', + { + get: function () { + return _pdsession ? _pdsession.services: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'serviceURI', + { + get: function () { + if (_pdsession && _pdsession.serviceURI) { + return _pdsession.serviceURI; + } + else { + return _serviceURI; + } + }, + enumerable: true + }); + + Object.defineProperty(this, 'userName', + { + get: function () { + return _pdsession ? _pdsession.userName: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'name', + { + get: function () { + return _name; + }, + enumerable: true + }); + + Object.defineProperty( + this, + "_isInvalidated", + { + get: function () { + return _pdsession._isInvalidated; + }, + enumerable: false + } + ); + + // PRIVATE FUNCTIONS + + + // Wrapper to make it easier to change the promise implementation we use. + // Note that in the JSDO library's first implementation of promise support, + // the "promise" parameter for this function is actually a jQuery Deferred object + function settlePromise(promise, fulfill, result, info) { + if (fulfill) { + promise.resolve(that, result, info); + } else { + promise.reject(that, result, info); + } + } + + // use this for the events fired by progress.data.Session that can be handled with common code + function genericSessionEventHandler(pdsession, result, errorObject, xhr, deferred) { + var myDeferred; + + if (xhr) { + myDeferred = xhr._deferred; + } else { + myDeferred = deferred; + } + + settlePromise(myDeferred, + result === progress.data.Session.SUCCESS ? true : false, + result, + { errorObject: errorObject, + xhr: xhr }); + } + + function onAfterAddCatalog( pdsession, result, errorObject, xhr ) { + var deferred, + fulfill = false, + settleResult; + + if (result === progress.data.Session.EXPIRED_TOKEN) { + settleResult = progress.data.Session.EXPIRED_TOKEN; + } else { + settleResult = progress.data.Session.GENERAL_FAILURE; + } + + if (xhr && xhr._deferred) { + deferred = xhr._deferred; + + /* add the result for this addCatalog to the result array. */ + if ( result !== progress.data.Session.SUCCESS && + result !== progress.data.Session.CATALOG_ALREADY_LOADED ) { + + result = result || progress.data.Session.GENERAL_FAILURE; + + /* Set a property on the deferred to indicates that the "overall" result was + a failure. When we decide whether to reject or resolve the promise, we reject + if it's set to GENERAL_FAILURE, otherwise we resolve the promise + (really only need to set this once, but simpler code if we just set (or possibly + re-set) it whenever we find an error, plus if, at some point while we're still + processing, it's important to know whether we've already had an error, we can + check the property) + */ + deferred._overallCatalogResult = progress.data.Session.GENERAL_FAILURE; + } + + deferred._results[xhr._catalogIndex] = { catalogURI : xhr._catalogURI, + result : result, + errorObject : errorObject, + xhr : xhr}; + deferred._numCatalogsProcessed += 1; + if ( deferred._numCatalogsProcessed === deferred._numCatalogs ) { + deferred._processedPromise = true; + + if ( !deferred._overallCatalogResult ) { + fulfill = true; + settleResult = progress.data.Session.SUCCESS; + } + settlePromise(xhr._deferred, + fulfill, + settleResult, + xhr._deferred._results); + } + } + } + + function onAfterLogout(pdsession, errorObject, xhr) { + var result = progress.data.Session.GENERAL_FAILURE, + fulfill = false; + if (xhr && xhr._deferred) { + /* Note: loginResult gets cleared on successful logout, so testing it for false + to confirm that logout succeeded + */ + if (!errorObject && !pdsession.loginResult) { + result = progress.data.Session.SUCCESS; + fulfill = true; + } + settlePromise(xhr._deferred, + fulfill, + result, + { errorObject: errorObject, + xhr: xhr }); + } + } + + function onPingComplete(args) { + var xhr = args.xhr; + if (xhr && xhr._deferred) { + settlePromise(xhr._deferred, + args.pingResult, // this tells settlePromise whether to resolve or reject + args.pingResult, // this is the result value passed to the promise handler + { offlineReason: args.offlineReason, + xhr: xhr }); + } + } + + // METHODS + + // login() + // Creates an AuthenticationProvider and calls its login() method. Any errors thrown by the + // Auth Provider's constructor or login will bubble up to the caller, otherwise this method + // returns the promise from the A-P's login call. + this.login = function (username, password, options) { + var deferred = $.Deferred(), + iOSBasicAuthTimeout; + + function callIsAuthorized() { + that.isAuthorized() + .then(function (jsdosession, result, info) { + deferred.resolve(that, result, info); + }, function (jsdosession, result, info) { + deferred.reject(that, result, info); + }); + } + + if (this._isInvalidated) { + // JSDOSession: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); + } + + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // JSDOSession: Cannot call login() when authenticationModel is SSO. + // Please use the AuthenticationProvider object instead. + throw new Error(progress.data._getMsgText("jsdoMSG057", + 'JSDOSession', + 'login()')); + } + + if (typeof options === 'object') { + iOSBasicAuthTimeout = options.iOSBasicAuthTimeout; + } + + if (!_pdsession._authProvider) { + // is there a better way to do this? Need it because we didn't have the authprovider when + // running the constructor + _pdsession._authProvider = new progress.data.AuthenticationProvider({ + uri: this.serviceURI, + authenticationModel: this.authenticationModel + }); + } + + + _pdsession._authProvider.logout() + .then( function () { + return _pdsession._authProvider.login(username, password); + }) + .then(function () { + callIsAuthorized(); + }, function (provider, result, info) { + deferred.reject(that, result, info); + }); + + return deferred.promise(); + }; + + // This method terminates the JSDOSession's ability to send requests to its serviceURI. + // Remove the reference to the AuthenticationProvider that was passed to connect(). + // Will be a no-op if connect() has not yet been called successfully. + // This method reinitializes the Session object back to the state it was in just after being created. + // Retains the serviceURI, authenticationModel, and name values. + // Delete any of the object's data that had been persisted (for example, to sessionStorage to support + // page refresh). + // Data for any catalogs loaded by the JSDOSession will NOT be deleted. + // See additional commecnts at the Session._disconnect method. + this.disconnect = function () { + var deferred = $.Deferred(), + errorObject; + + try { + _pdsession.subscribe('afterDisconnect', genericSessionEventHandler, this); + + _pdsession._disconnect(deferred); + } catch (e) { + // JSDOSession: Unexpected error calling disconnect: {e.message} + errorObject = new Error(progress.data._getMsgText("jsdoMSG049", "JSDOSession", "disconnect", e.message)); + } + + if (errorObject) { + throw errorObject; + } else { + return deferred.promise(); + } + }; + + this.addCatalog = function (catalogURI, unameOrOpts, password, opts) { + var deferred = $.Deferred(), + catalogURIs, + numCatalogs, + catalogIndex, + addResult, + errorObject, + iOSBasicAuthTimeout, + username, + options, + authProvider; + + if (this._isInvalidated) { + // JSDOSession: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); + } + + // check whether 1st param is a string or an array + if (typeof catalogURI === "string") { + catalogURIs = [catalogURI]; + } else if (catalogURI instanceof Array) { + catalogURIs = catalogURI; + } else { + throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "addCatalog", + "The first argument must be a string or an array of strings specifying the URI of the catalog.")); + } + + // type check the 2nd param if it exists + if (unameOrOpts) { + if (typeof unameOrOpts === "string") { + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // Session: Cannot pass username and password to addCatalog when + // authenticationModel is SSO. Pass an AuthenticationProvider instead. + throw new Error(progress.data._getMsgText("jsdoMSG058", 'Session')); + } + username = unameOrOpts; + // explictly ignore any authProvider if using the (catURI, uname, pw, options) signature + if (opts) { + options = opts; + options.authProvider = undefined; + } + } else if (typeof unameOrOpts === "object") { + options = unameOrOpts; + } else { + // JSDOSession: Argument 2 must be of type object in addCatalog call. + throw new Error(progress.data._getMsgText("jsdoMSG121", "JSDOSession", "2", + "object", "addCatalog")); + } + } + + if (typeof options === 'object') { + // possible override for the workaround for the Cordova iOS async Basic auth bug + iOSBasicAuthTimeout = options.iOSBasicAuthTimeout; + if (options.authProvider) { + authProvider = options.authProvider; + } else if (this.authProvider) { + authProvider = this.authProvider; + } + } + + // Error out if no authProvider or username was given + if (!authProvider && !this.authProvider && !username) { + throw new Error(progress.data._getMsgText("jsdoMSG511")); + } + + /* When we're done processing all catalogs, we pass an array of results to resolve() or + reject(). We're attaching this array to the deferred object, in case the app makes + multiple addCatalog calls (if the array was attached to the JSDOSession, + the 2nd call might overwrite the first) + */ + + /* Add properties to the deferred object for this call to store the total + number of catalogs that are to be done, the number that ahve been processed, + and a reference to an array of results. + Loop through the array of catalogURIs, calling addCatalog for each one. If a call + throws an error or returns something other than ASYNC_PENDING, create a result object + for that catalog and add the result object to the resultArray. Otherwise, the result + object will be added by the afterAddCatalog handler. + If all of the Session.addCatalog calls throw an error or return something other + than ASYNC_PENDING, this function will reject the promise and return. Otherwise + the afterAddCatalog handler will resolve or reject the promise after all calls have + been processed. + Note that we try to make sure that each entry in the results array is in the same position + as its catalogURI in the input array. + */ + // if a catalogURI has no protocol, pdsession will assume it's relative to the serviceURI, + // if there has been a login + // NOTE: this means if the app is trying to load a local catalog, it MUST + // specify the file: protocol (and we need to make sure that works on all platforms) + + _pdsession.subscribe('afterAddCatalog', onAfterAddCatalog, this); + + numCatalogs = catalogURIs.length; + deferred._numCatalogs = numCatalogs; + deferred._numCatalogsProcessed = 0; + deferred._results = []; + deferred._results.length = numCatalogs; + + for ( catalogIndex = 0; catalogIndex < numCatalogs; catalogIndex += 1) { + errorObject = undefined; + addResult = undefined; + try { + addResult = _pdsession.addCatalog( + { catalogURI : catalogURIs[catalogIndex], + async : true, + userName : username, + password : password, + deferred : deferred, + catalogIndex : catalogIndex, + iOSBasicAuthTimeout : iOSBasicAuthTimeout, + authProvider : authProvider, + offlineAddCatalog : true } ); // OK to get catalog if offline + } + catch (e) { + errorObject = new Error("JSDOSession: Unable to send addCatalog request. " + e.message); + } + + if ( addResult !== progress.data.Session.ASYNC_PENDING ) { + /* Set a property on the deferred to indicate that the "overall" result was + a failure. When we decide whether to reject or resolve the promise, we reject + if it's set to GENERAL_FAILURE, otherwise we resolve the promise + (really only need to set this once, but simpler code if we just set (or possibly + re-set) it whenever we find an error, plus if, at some point while we're still + processing, it's important to know whether we've already had an error, we can + check the property) + */ + deferred._overallCatalogResult = progress.data.Session.GENERAL_FAILURE; + if ( errorObject ) { + addResult = progress.data.Session.GENERAL_FAILURE; + } + deferred._results[catalogIndex] = { catalogURI : catalogURIs[catalogIndex], + result : addResult, + errorObject : errorObject, + xhr : undefined }; + deferred._numCatalogsProcessed += 1; + } + } + + if ( (deferred._numCatalogsProcessed === numCatalogs) && !deferred._processedPromise ) { + /* The goal here is to handle the case where all the catalogs + have been processed but the afterAddCatalog handler may not be invoked at the + end (the obvious example is if there are no async requests actually made by + Session.addCatalog). In that case, we have to resolve/reject from here. Chances are + very good that if we're doing this here, there's been at least one error, but just + to be sure, we check the deferred._overallCatalogResult anyway + */ + if ( deferred._overallCatalogResult === progress.data.Session.GENERAL_FAILURE ) { + deferred.reject( this, progress.data.Session.GENERAL_FAILURE, deferred._results ); + } + else { + deferred.resolve( this, progress.data.Session.SUCCESS, deferred._results ); + } + } + + return deferred.promise(); + }; + + // Note that this will work for either of these cases: + // - app originally called JSDOSession.login (so we implicitly created the AuthenticationProvider) + // - app created an AuthenticationProvider and passed it to connect, but now for some reason has + // called logout (this is actually a nice shortcut for someone who has used getSession) + // (NB: we should not allow this for SSO, tho) + // + // Note that we also don't support login/logout on the JSDOSession for page refresh + this.logout = function(){ + var deferred = $.Deferred(), + authProv = this.authProvider; + + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // JSDOSession: Cannot call logout() when authenticationModel is SSO. + // Please use the AuthenticationProvider object instead. + throw new Error(progress.data._getMsgText("jsdoMSG057", + 'JSDOSession', + 'logout()')); + } + + this.disconnect() + .then(function () { + if (authProv) { + return authProv.logout(); + } + // if there's no AP, just resolve immediately + deferred.resolve(that, progress.data.Session.SUCCESS, {}); + }) + .then(function (jsdosession, result, info) { + deferred.resolve(that, result, info); + }, + // catches errors on either login or connect + function (provider, result, info) { + deferred.reject(that, result, info); + } + ); + + return deferred.promise(); + }; + + this.invalidate = function () { + _pdsession.invalidate(); + return this.logout(); + }; + + this.ping = function() { + var deferred = $.Deferred(); + + if (this._isInvalidated) { + // JSDOSession: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); + } + + try { + _pdsession.ping( {async: true, + deferred : deferred, + onCompleteFn : onPingComplete } ); + } + catch(e) { + throw new Error("JSDOSession: Unable to send ping request. " + e.message); + } + + return deferred.promise(); + }; + + // Determine whether the JSDOSession can currently access its web application. + // The use expected for this method is to determine whether a JSDOSession that has + // previously authenticated to its web application still has authorization. + // For example, if the JSDOSession is using Form authentication, is the server + // session still valid or did it expire? + this.isAuthorized = function () { + var deferred = $.Deferred(), + xhr = new XMLHttpRequest(), + result, + that = this; + + if (this._isInvalidated) { + // JSDOSession: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); + } + + // If we logged in successfuly using login() or if we have an AuthProvider, make the call + if (this.loginResult === progress.data.Session.LOGIN_SUCCESS || this.authProvider) { + _pdsession._openRequest(xhr, "GET", _pdsession.loginTarget, true, + function () { + xhr.onreadystatechange = function () { + // do we need this xhr var? The one declared in isAuthorized seems to be in scope + var xhr = this, + cbresult, + fakePingArgs, + info; + + if (xhr.readyState === 4) { + info = {xhr: xhr, + offlineReason: undefined, + fireEventIfOfflineChange: true, + usingOepingFormat: false + }; + + // call _processPingResult because it has logic for + // detecting change in online/offline state + _pdsession._processPingResult(info); + + if (xhr.status >= 200 && xhr.status < 300) { + deferred.resolve(that, + progress.data.Session.SUCCESS, + info); + } else { + if (xhr.status === 401) { + cbresult = progress.data.AuthenticationProvider._getAuthFailureReason(xhr); + } else { + cbresult = progress.data.Session.GENERAL_FAILURE; + } + deferred.reject(that, cbresult, info); + } + } + }; + + try { + xhr.send(); + } catch (e) { + throw new Error("JSDOSession: Unable to validate authorization. " + e.message); + } + } + ); + } else { + // Never logged in (or logged in and logged out). Regardless of what the reason + // was that there wasn't a login, the bottom line is that authentication is required + result = progress.data.Session.LOGIN_AUTHENTICATION_REQUIRED; + deferred.reject(that, result, {xhr: xhr}); + } + + return deferred.promise(); + }; + + /* + set the properties that are passed between client and Web application in the + X-CLIENT-PROPS header. This sets the complete set of properties all at once; + it replaces any existing context + */ + this.setContext = function( context ) { + _pdsession._contextProperties.setContext( context ); + }; + + /* + * Set or remove an individual property in the set of the properties that are passed + * between client and Web application in the X-CLIENT-PROPS header. This operates only + * on the property identiofied by propertyName; all other existing properties remain + * as they are. + * If the propertyName is not part of the context, this call adds it + * If it is part of the context, this call updates it, unless - + * If propertyValue is undefined, this call removes the property + */ + this.setContextProperty = function( propertyName, propertyValue) { + _pdsession._contextProperties.setContextProperty( propertyName, propertyValue ); + }; + + /* + * get the set of properties that are passed between client and Web application in the + * X-CLIENT-PROPS header. Returns an object that has the properties + */ + this.getContext = function( ) { + return _pdsession._contextProperties.getContext(); + }; + + /* get the value of an individual property that is in the set of properties passed between + * client and Web application in the X-CLIENT-PROPS header + */ + this.getContextProperty = function( propertyName) { + return _pdsession._contextProperties.getContextProperty( propertyName ); + }; + + + this._onlineHandler = function( session, request ) { + that.trigger( "online", that, request ); + }; + + this._offlineHandler = function( session, offlineReason, request ) { + that.trigger( "offline", that, offlineReason, request ); + }; + + // PROCESS CONSTRUCTOR ARGUMENTS + // validate constructor input arguments + if ( (arguments.length > 0) && (typeof(arguments[0]) === 'object') ) { + + // (options is the name of the arguments[0] parameter) + if (options.serviceURI && (typeof(options.serviceURI) === "string" ) ) { + _serviceURI = options.serviceURI; + } + else { + throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", + "The options parameter must include a 'serviceURI' property that is a string.") ); + } + + if (options.authenticationModel) { + if (typeof(options.authenticationModel) !== "string" ) { + throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", + "The authenticationModel property of the options parameter must be a string.") ); + } + + options.authenticationModel = options.authenticationModel.toLowerCase(); + } else { + options.authenticationModel = progress.data.Session.AUTH_TYPE_ANON; + } + + // TODO: clean this up. Maybe make an immediate function + if (options.authProvider) { + if (typeof options.authProvider !== 'object') { + // JSDOSession: The 'options' parameter passed to the 'constructor' function + // has an invalid value for the 'authProvider' property. + throw new Error(progress.data._getMsgText( + "jsdoMSG502", + "JSDOSession", + "options", + "constructor", + "authProvider" + )); + } + + if ((options.authProvider.authenticationModel !== progress.data.Session.AUTH_TYPE_FORM_SSO + && options.authProvider.authenticationModel !== options.authenticationModel) || + (options.authProvider.authenticationModel === progress.data.Session.AUTH_TYPE_FORM_SSO + && options.authenticationModel !== progress.data.Session.AUTH_TYPE_SSO)) { + // JSDOSession: Error in constructor. The authenticationModels of the " + + // AuthenticationProvider ({2}) and the JSDOSession ({3}) were not compatible."; + throw new Error(progress.data._getMsgText("jsdoMSG059", "JSDOSession", + options.authProvider.authenticationModel, options.authenticationModel)); + } + // Check if the provider exposes the required API. + if (typeof options.authProvider.hasClientCredentials === 'function') { + if (!options.authProvider.hasClientCredentials()) { + // JSDOSession: The AuthenticationProvider is not managing valid credentials. + throw new Error(progress.data._getMsgText("jsdoMSG125", "JSDOSession")); + } + } else { + // JSDOSession: AuthenticationProvider objects must have a hasClientCredentials method. + throw new Error(progress.data._getMsgText("jsdoMSG505", + "JSDOSession", + "AuthenticationProvider", + "hasClientCredentials")); + } + } else if (options.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // JSDOSession: If a JSDOSession object is using the SSO authentication model, + // the options object passed to its constructor must include an authProvider property. + throw new Error(progress.data._getMsgText("jsdoMSG508")); + } + + } + else { + throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", + "The options argument was missing or invalid.") ); + } + + _name = options.name; + + _pdsession = new progress.data.Session({_storageKey: _name, + _silent: true, + authenticationModel: options.authenticationModel, + serviceURI: options.serviceURI, + jsdosession: this, + authProvider: options.authProvider}); + + try { + if (options.context) { + this.setContext(options.context); + } + _pdsession.subscribe( "online", this._onlineHandler, this); + _pdsession.subscribe( "offline", this._offlineHandler, this); + } catch (err) { + _pdsession = undefined; // so it will be garbage collected + throw err; + } + + }; // end of JSDOSession + +//set up inheritance for JSDOSession -- specifically for incorporating an Observable object + progress.data.JSDOSession.prototype = new progress.util.Observable(); + progress.data.JSDOSession.prototype.constructor = progress.data.JSDOSession; + function validateJSDOSessionSubscribe(args, evt, listenerData) { + listenerData.operation = undefined; + var found = false; + + // make sure this event is one that we support + for (var i = 0; i < this._eventNames.length; i++) { + if (evt === this._eventNames[i].toLowerCase()) { + found = true; + break; + } + } + if (!found) { + throw new Error(progress.data._getMsgText("jsdoMSG042", evt)); + } + + if (args.length < 2) { + throw new Error(progress.data._getMsgText("jsdoMSG038", 2)); + } + + if (typeof args[0] !== 'string') { + throw new Error(progress.data._getMsgText("jsdoMSG039")); + } + + if (typeof args[1] !== 'function') { + throw new Error(progress.data._getMsgText("jsdoMSG040")); + } + else { + listenerData.fn = args[1]; + } + + if (args.length > 2) { + if (typeof args[2] !== 'object') { + throw new Error(progress.data._getMsgText("jsdoMSG041", evt)); + } + else { + listenerData.scope = args[2]; + } + } + } + // events supported by JSDOSession + progress.data.JSDOSession.prototype._eventNames = + ["offline", "online"]; + // callback to validate subscribe and unsubscribe + progress.data.JSDOSession.prototype.validateSubscribe = validateJSDOSessionSubscribe; + progress.data.JSDOSession.prototype.toString = function (radix) { + return "progress.data.JSDOSession"; + }; + + progress.data.getSession = function (options) { + var deferred = $.Deferred(), + authProvider, + promise, + authProviderInitObject = {}; + + // This is the reject handler for session-related operations + // login, addCatalog, and logout + function sessionRejectHandler(originator, result, info) { + // undo the AuthenticationProvider's login if it succeeded + if (authProvider && authProvider.hasClientCredentials()) { + authProvider.logout() + .always(function () { + deferred.reject(result, info); + }); + } else { + deferred.reject(result, info); + } + } + + // This is the reject handler for the login callback + function callbackRejectHandler(reason) { + deferred.reject(progress.data.Session.GENERAL_FAILURE, {"reason": reason}); + } + + function loginHandler(provider) { + var jsdosession; + + try { + jsdosession = new progress.data.JSDOSession(options); + try { + jsdosession.isAuthorized() + .then(function() { + return jsdosession.addCatalog(options.catalogURI); + }, sessionRejectHandler) + .then(function (jsdosession, result, info) { + deferred.resolve(jsdosession, progress.data.Session.SUCCESS); + }, sessionRejectHandler); + } catch (e) { + sessionRejectHandler(jsdosession, + progress.data.Session.GENERAL_FAILURE, + {errorObject: e}); + } + } catch (e) { + sessionRejectHandler(jsdosession, + progress.data.Session.GENERAL_FAILURE, + {errorObject: e}); + } + } + + // This function calls login using credentials from the appropriate source + // Note that as currently implemented, this should NOT be called when + // ANONYMOUS auth is being used, because it unconditionally returns + // AUTHENTICATION_FAILURE if there are no credentials and no loginCallback + function callLogin(provider) { + var errorObject; + + // Use the login callback if we are passed one + // NOTE: Do we even use logincallback? Remove this??? + if (typeof options.loginCallback !== 'undefined') { + options.loginCallback() + .then(function (result) { + try { + provider.login(result.username, result.password) + .then(loginHandler, sessionRejectHandler); + } catch (e) { + sessionRejectHandler( + provider, + progress.data.Session.GENERAL_FAILURE, + { + errorObject: e + } + ); + } + }, callbackRejectHandler); + } else if (options.username && options.password) { + try { + provider.login(options.username, options.password) + .then(loginHandler, sessionRejectHandler); + } catch (e) { + sessionRejectHandler( + provider, + progress.data.Session.GENERAL_FAILURE, + { + errorObject: e + } + ); + } + } else { + // getSession(): The login method was not executed because no credentials were supplied. + errorObject = new Error(progress.data._getMsgText( + "jsdoMSG052", + "getSession()" + )); + sessionRejectHandler( + provider, + progress.data.Session.AUTHENTICATION_FAILURE, + { + // including an Error object to make clear why there is no xhr (normally there would + // be one for an authentication failure) + errorObject: errorObject + } + ); + } + } + + if (typeof options !== 'object') { + // getSession(): 'options' must be of type 'object' + throw new Error(progress.data._getMsgText( + "jsdoMSG503", + "getSession()", + "options", + "object" + )); + } + + if (typeof options.loginCallback !== 'undefined' && + typeof options.loginCallback !== 'function') { + // getSession(): 'options.loginCallback' must be of type 'function' + throw new Error(progress.data._getMsgText( + "jsdoMSG503", + "getSession()", + "options.loginCallback", + "function" + )); + } + + // Create the AuthenticationProvider and let it handle the argument parsing + try { + // If authenticationURI is not set, use serviceURI (except for SSO) + // Note: the test will of course catch any value that evaluates to false, not just undefined or + // null (which are the main concern), but that's probably OK + if (options.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + if (!options.authenticationURI || !options.authProviderAuthenticationModel) { + // "progress.data.getSession: If the getSession method is passed AUTH_TYPE_SSO as + // the authenticationModel, it must also be passed an authenticationURI and an + // authProviderAuthenticationModel." + throw new Error(progress.data._getMsgText("jsdoMSG509")); + } + } + + if (options.authenticationURI) { + authProviderInitObject.uri = options.authenticationURI; + authProviderInitObject.authenticationModel = options.authProviderAuthenticationModel; + + // if auth uri has been passed, there must be an authProviderAuthenticationModel + if (typeof authProviderInitObject.authenticationModel !== "string") { + // JSDOSession: The 'object' parameter passed to the 'getSession' function + // has an invalid value for the 'authProviderAuthenticationModel' property. + throw new Error(progress.data._getMsgText( + "jsdoMSG502", + "progress.data.getSession", + "object", + "getSession", + "authProviderAuthenticationModel" + )); + } + } else { + authProviderInitObject.uri = options.serviceURI; + authProviderInitObject.authenticationModel = options.authenticationModel; + } + + authProvider = new progress.data.AuthenticationProvider(authProviderInitObject); + options.authProvider = authProvider; + + if (authProvider.hasClientCredentials()) { + loginHandler(authProvider); + } else { + // If model is anon, just log in. + if (authProvider.authenticationModel === progress.data.Session.AUTH_TYPE_ANON) { + authProvider.login() + .then(loginHandler, sessionRejectHandler); + } else { + // We need to log-in with credentials. + callLogin(authProvider); + } + } + } catch (error) { + // throw error; + sessionRejectHandler( + null, + progress.data.Session.GENERAL_FAILURE, + { + errorObject: error + } + ); + } + + return deferred.promise(); + }; + + progress.data.invalidateAllSessions = function () { + var jsdosession, + key, + deferred = $.Deferred(), + jsdosessions = progress.data.ServicesManager._jsdosessions, + invalidatePromises = []; + + for (key in jsdosessions) { + if (jsdosessions.hasOwnProperty(key)) { + jsdosession = jsdosessions[key]; + + invalidatePromises.push(jsdosession.invalidate()); + } + } + + $.when.apply($, invalidatePromises) + .then(function () { + deferred.resolve(progress.data.Session.SUCCESS); + }, function (session, result, info) { + deferred.reject(progress.data.Session.GENERAL_FAILURE, info); + }); + + // Using beautiful jquery shenanigans + return deferred.promise(); + }; + +})(); + +if (typeof exports !== "undefined") { + exports.progress = progress; +} + +//# sourceURL=progress.jsdo.js +/* +progress.auth.js Version: 4.4.0-3 + +Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + "use strict"; // note that this makes JSLint complain if you use arguments[x] + + /*global progress : true*/ + /*global $ : false, storage, XMLHttpRequest*/ + + /* define these if not defined yet - they may already be defined if + progress.js was included first */ + if (typeof progress === "undefined") { + progress = {}; + } + if (typeof progress.data === "undefined") { + progress.data = {}; + } + + // This is really more along the lines of a Factory method in that it explicitly creates an object + // and returns it based on the the authModel parameter (rather than following the default JS + // pattern of adding properties to the "this" object created for it and passed in by the runtime). + // NOTE: If we support multiple AuthenticationProviders that get different tokens from the same + // server, we may need to add a "name" property to the initObject to use as a storage key + + progress.data.AuthenticationProvider = function (initObject) { + var authProv, + authModel, + uri; + + // process constructor arguments + if (typeof initObject === 'object') { + + // these 2 calls throw an appropriate error if the check doesn't pass + this._checkStringArg( + "constructor", + initObject.authenticationModel, + "initObject.authenticationModel", + "initObject.authenticationModel" + ); + + this._checkStringArg( + "constructor", + initObject.uri, + "init-object.uri", + "init-object.uri" + ); + } else { + // AuthenticationProvider: Invalid signature for constructor. The init-object argument + // was missing or invalid. + throw new Error(progress.data._getMsgText( + "jsdoMSG033", + "AuthenticationProvider", + "the constructor", + "The init-object argument was missing or invalid." + )); + } + + authModel = initObject.authenticationModel.toLowerCase(); + switch (authModel) { + case progress.data.Session.AUTH_TYPE_ANON: + this._initialize(initObject.uri, progress.data.Session.AUTH_TYPE_ANON, + {"_loginURI": progress.data.AuthenticationProvider._homeLoginURIBase}); + authProv = this; + break; + case progress.data.Session.AUTH_TYPE_BASIC: + authProv = new progress.data.AuthenticationProviderBasic(initObject.uri); + break; + case progress.data.Session.AUTH_TYPE_FORM: + authProv = new progress.data.AuthenticationProviderForm(initObject.uri); + break; + case progress.data.Session.AUTH_TYPE_FORM_SSO: + authProv = new progress.data.AuthenticationProviderSSO(initObject.uri); + break; + default: + // AuthenticationProvider: The 'init-object' parameter passed to the 'constructor' function + // has an invalid value for the 'authenticationModel' property. + throw new Error(progress.data._getMsgText( + "jsdoMSG502", + "AuthenticationProvider", + "init-object", + "constructor", + "authenticationModel" + )); + //break; + } + + return authProv; + }; + + + // ADD METHODS TO THE AuthenticationProvider PROTOYPE + + // GENERIC IMPLEMENTATION FOR login METHOD THAT THE API IMPLEMENTATIONS OF login CAN CALL + // (technically, they don't override it, they each have small login methods that call this) + progress.data.AuthenticationProvider.prototype._loginProto = + function (sendParam) { + var deferred = $.Deferred(), + xhr, + uriForRequest, + header, + that = this; + + if (this._loggedIn) { + // "The login method was not executed because the AuthenticationProvider is + // already logged in." + throw new Error(progress.data._getMsgText("jsdoMSG051", "AuthenticationProvider")); + } + + xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // process the response from the Web application + that._processLoginResult(xhr, deferred); + } + }; + + if (progress.data.Session._useTimeStamp) { + uriForRequest = progress.data.Session._addTimeStampToURL(this._loginURI); + } else { + uriForRequest = this._loginURI; + } + + this._openLoginRequest(xhr, uriForRequest); + + // We specify application/json for the response so that, if a bad request is sent, an + // OE Web application will directly send back a 401 with error info in the body as JSON. + // So we force the accept header to application/json because if we make an anonymous + // request to a FORM/BASIC backend, it might redirect us to a login page since we have + // no credentials. And since we can technically access JUST the login page, the XHR + // will identify it as SUCCESS. If we specify "application/json", no redirects will + // happen, just a plain old "401 GET OUTTA HERE" code. + xhr.setRequestHeader("Accept", "application/json"); + + xhr.send(sendParam); + return deferred.promise(); + }; + + + + // PUBLIC METHODS (and their "helpers") (documented as part of the JSDO library API) + + // login API method -- just a shell that calls loginProto + progress.data.AuthenticationProvider.prototype.login = function () { + return this._loginProto(); + }; + + // HELPER FOR login METHOD, PROBABLY OVERRIDDEN IN MOST CONSTRUCTORS + progress.data.AuthenticationProvider.prototype._openLoginRequest = function (xhr, uri) { + xhr.open('GET', uri, true); + progress.data.Session._setNoCacheHeaders(xhr); + }; + + // HELPER FOR login METHOD, PROBABLY OVERRIDDEN IN MOST CONSTRUCTORS + progress.data.AuthenticationProvider.prototype._processLoginResult = function (xhr, deferred) { + var result; + + if (xhr.status === 200) { + // Need to set loggedIn now so we can call logout from here if there's an + // error processing the response (e.g., authentication succeeded but we didn't get a + // token for some reason) + this._loggedIn = true; + this._storeInfo(); + result = progress.data.Session.SUCCESS; + } else if (xhr.status === 401) { + // If this is Anonymous, somebody gave us the wrong authenticationModel! + result = progress.data.Session.AUTHENTICATION_FAILURE; + } else { + result = progress.data.Session.GENERAL_FAILURE; + } + + this._settlePromise(deferred, result, {"xhr": xhr}); + }; + + + // logout API METHOD -- SOME CONSTRUCTORS OR PROTOTYPES WILL OVERRIDE THIS + progress.data.AuthenticationProvider.prototype.logout = function () { + var deferred = $.Deferred(); + + this._reset(); + deferred.resolve(this, progress.data.Session.SUCCESS, {}); + return deferred.promise(); + }; + + // hasClientCredentials API METHOD -- PROBABLY ONLY OVERRIDDEN BY SSO + progress.data.AuthenticationProvider.prototype.hasClientCredentials = function () { + return this._loggedIn; + }; + + // hasRefreshToken API METHOD -- returns false for all AutghenticationProvider types except SSO, + // which overrides it + progress.data.AuthenticationProvider.prototype.hasRefreshToken = function () { + return false; + }; + + // QUASI-PUBLIC METHOD + + // general-purpose method for opening requests (mainly for jsdo calls) + // This method is not part of the documented API that a developer would + // program against, but it gets used in a validation check by the JSDOSESSION, because the + // JSDOSESSION code expects it to be present. The point here is that if a developer were to + // create their own AuthenticationProvider object, it would need to include this method + // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change + // it to use promises + progress.data.AuthenticationProvider.prototype._openRequestAndAuthorize = function (xhr, + verb, + uri, + async, + callback) { + var errorObject; + + if (this.hasClientCredentials()) { + xhr.open(verb, uri, async); + + // Check out why we do this in _loginProto + xhr.setRequestHeader("Accept", "application/json"); + } else { + // AuthenticationProvider: The AuthenticationProvider is not managing valid credentials. + errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); + } + + callback(errorObject); + }; + + // GENERAL PURPOSE "INTERNAL" METHODS, NOT RELATED TO SPECIFIC API ELEMENTS + // (not documented, intended for use only within the JSDO library) + + // General purpose method for initializing an object + progress.data.AuthenticationProvider.prototype._initialize = function (uriParam, + authModel, + targetURIs) { + var tempURI, + target; + + Object.defineProperty(this, 'uri', + { + get: function () { + return this._uri; + }, + enumerable: true + }); + + Object.defineProperty(this, 'authenticationModel', + { + get: function () { + return this._authenticationModel; + }, + enumerable: true + }); + + + // get rid of trailing '/' because appending service url that starts with '/' + // will cause request failures + if (uriParam[uriParam.length - 1] === "/") { + tempURI = uriParam.substring(0, uriParam.length - 1); + } else { + tempURI = uriParam; + } + + // take the modified authentication uri and prepend it to all of the targets passed + // in. E.g., the targetURIs object will include a "loginURI" property that has the + // uri segment which is to be added to the auth uri for logging in + for (target in targetURIs) { + if (targetURIs.hasOwnProperty(target)) { + this[target] = tempURI + targetURIs[target]; + } + } + + this._authenticationModel = authModel; + this._uri = uriParam; // keep the uri property the same as what was passed in + + this._loggedIn = false; + this._dataKeys = { + uri: ".uri", + loggedIn: ".loggedIn" + }; + + // future: for page refresh -- storeSessionInfo("authenticationModel", authenticationModel); + + if (typeof sessionStorage === "undefined") { + // "AuthenticationProvider: No support for sessionStorage." + throw new Error(progress.data._getMsgText("jsdoMSG126", + "AuthenticationProvider", + "sessionStorage")); + } + // if you switch to a different type of storage, change the error message argument above + this._storage = sessionStorage; + + // maybe should come up with something more intelligent than this + this._storageKey = this._uri; // or name + this._dataKeys.uri = this._storageKey + this._dataKeys.uri; + this._dataKeys.loggedIn = this._storageKey + this._dataKeys.loggedIn; + + if (this._retrieveLoggedIn()) { + this._loggedIn = true; + } + }; + + + // Store data in storage with the uri as the key. setItem() throws. (Should add an + // option for the developer to specify the key) + // a "QuotaExceededError" error if there is insufficient storage space or + // "the user has disabled storage for the site" (Web storage spec at WHATWG) + progress.data.AuthenticationProvider.prototype._storeInfo = function () { + this._storage.setItem(this._dataKeys.uri, JSON.stringify(this._uri)); + this._storage.setItem(this._dataKeys.loggedIn, JSON.stringify(this._loggedIn)); + }; + + // Get a piece of state data from storage. Returns null if the item isn't in storage + progress.data.AuthenticationProvider.prototype._retrieveInfoItem = function (propName) { + var jsonStr = this._storage.getItem(propName), + value = null; + + if (jsonStr !== null) { + try { + value = JSON.parse(jsonStr); + } catch (e) { + value = null; + } + } + return value; + }; + + // Get an AuthenticationProvider's uri from storage + progress.data.AuthenticationProvider.prototype._retrieveURI = function () { + return this._retrieveInfoItem(this._dataKeys.uri); + }; + + // Get an AuthenticationProvider's logon status from storage + progress.data.AuthenticationProvider.prototype._retrieveLoggedIn = function () { + return this._retrieveInfoItem(this._dataKeys.loggedIn); + }; + + // Clear the persistent storage used by an AuthenticationProvider + progress.data.AuthenticationProvider.prototype._clearInfo = function (info) { + this._storage.removeItem(this._dataKeys.uri); + this._storage.removeItem(this._dataKeys.loggedIn); + }; + + // Put the internal state back to where it is when the constructor finishes + // running (so the authentication model and uri are not changed, but other data is reset. + // and storage is cleared out) + progress.data.AuthenticationProvider.prototype._reset = function () { + this._clearInfo(); + this._loggedIn = false; + }; + + + // General purpose utility method, no overrides expected + progress.data.AuthenticationProvider.prototype._settlePromise = function (deferred, result, info) { + if (result === progress.data.Session.SUCCESS) { + deferred.resolve(this, result, info); + } else { + deferred.reject(this, result, info); + } + }; + + // General purpose utility method, no overrides expected + progress.data.AuthenticationProvider.prototype._checkStringArg = function (fnName, + argToCheck, + argPosition, + argName) { + // TODO: ? distinguish between undefined (so we can give developer a clue that they + // may be missing a property) and defined but wrong type + if (typeof argToCheck !== "string") { + // AuthenticationProvider: Argument {param-position} must be of type string in {fnName} call. + throw new Error(progress.data._getMsgText( + "jsdoMSG121", + "AuthenticationProvider", + argPosition, + "string", + fnName + )); + } else if (argToCheck.length === 0) { + // AuthenticationProvider: {param-name} cannot be an empty string. + throw new Error(progress.data._getMsgText( + "jsdoMSG501", + "AuthenticationProvider", + argName, + fnName + )); + } + }; + + + // "STATIC" PROPERTIES AND METHODS -- not on the prototype -- you cannot access these through an + // object created by "new" --- they are properties of the AuthenticationProvider constructor function + + // Takes an XHR as an input. If the xhr status is 401 (Unauthorized), determines whether + // the auth failure was due to an expired token. Returns progress.data.Session.EXPIRED_TOKEN + // if it was, progress.data.Session.AUTHENTICATION_FAILURE if it wasn't, null if the xhr status wasn't 401 + progress.data.AuthenticationProvider._getAuthFailureReason = function (xhr) { + var contentType, + jsonObject, + result = progress.data.Session.AUTHENTICATION_FAILURE; + + if (xhr.status === 401) { + contentType = xhr.getResponseHeader("Content-Type"); + if (contentType && (contentType.indexOf("application/json") > -1) && xhr.responseText) { + jsonObject = JSON.parse(xhr.responseText); + if (jsonObject.error === "sso.token.expired_token") { + result = progress.data.Session.EXPIRED_TOKEN; + } + } + } else { + result = null; + } + return result; + }; + + Object.defineProperty(progress.data.AuthenticationProvider, '_homeLoginURIBase', { + value: "/static/home.html", + enumerable: true + }); + Object.defineProperty(progress.data.AuthenticationProvider, '_springLoginURIBase', { + value: "/static/auth/j_spring_security_check", + enumerable: true + }); + Object.defineProperty(progress.data.AuthenticationProvider, '_springLogoutURIBase', { + value: "/static/auth/j_spring_security_logout", + enumerable: true + }); + Object.defineProperty(progress.data.AuthenticationProvider, '_springFormTokenLoginURIBase', { + value: progress.data.AuthenticationProvider._springLoginURIBase + "?OECP=yes", + enumerable: true + }); + Object.defineProperty(progress.data.AuthenticationProvider, '_springFormTokenRefreshURIBase', { + value: "/static/auth/token?op=refresh", + enumerable: true + }); + +}()); + +//# sourceURL=progress.jsdo.js +/* +progress.auth.basic.js Version: 4.4.0-3 + +Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + "use strict"; // note that this makes JSLint complain if you use arguments[x] + + /*global progress : true*/ + /*global $ : false, storage, XMLHttpRequest, msg, btoa*/ + + progress.data.AuthenticationProviderBasic = function (uri) { + var defaultiOSBasicAuthTimeout, // TO DO: need to implement the use of this + userName = null, + password = null, + fn; + + // process constructor arguments, etc. + this._initialize(uri, progress.data.Session.AUTH_TYPE_BASIC, + {"_loginURI": progress.data.AuthenticationProvider._homeLoginURIBase}); + + // PRIVATE FUNCTIONS + + // from http://coderseye.com/2007/how-to-do-http-basic-auth-in-ajax.html + function make_basic_auth_header(user, pw) { + var tok = user + ':' + pw, + hash = btoa(tok); + return "Basic " + hash; + } + + // "INTERNAL" METHODS + // Override the protoype's method but call it from within the override + // (Define the override here in the constructor so it has access to instance variables) + this._reset = function () { + userName = null; + password = null; + progress.data.AuthenticationProviderBasic.prototype._reset.apply(this); + }; + + // Override the protoype's method (this method does not invoke the prototype's copy) + // (Define the override here in the constructor so it has access to instance variables) + this._openLoginRequest = function (xhr, uri) { + var auth; + + xhr.open("GET", uri, true); // but see comments below inside the "if userName" + // may have to go with that approach + + if (userName) { + + // set Authorization header + auth = make_basic_auth_header(userName, password); + xhr.setRequestHeader('Authorization', auth); + } + + progress.data.Session._setNoCacheHeaders(xhr); + }; + + // Override the protoype's method but call it from within the override + // (Define the override here in the constructor so it has access to instance variables) + this._processLoginResult = function _basic_processLoginResult(xhr, deferred) { + progress.data.AuthenticationProviderBasic.prototype._processLoginResult.apply( + this, + [xhr, deferred] + ); + if (!this._loggedIn) { + // login failed, clear the credentials + userName = null; + password = null; + } + }; + + // Override the protoype's method (this method does not invoke the prototype's copy, but + // calls a prototype general-purpose login method) + // (Define the override here in the constructor so it has access to instance variables) + this.login = function (userNameParam, passwordParam) { + // these throw if the check fails (may want to do something more elegant) + this._checkStringArg("login", userNameParam, 1, "userName"); + this._checkStringArg("login", passwordParam, 2, "password"); + + userName = userNameParam; + password = passwordParam; + return this._loginProto(); + }; + + // Override the protoype's method (this method does not invoke the prototype's copy) + // (Define the override here in the constructor so it has access to instance variables) + // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change + // it to use promises + this._openRequestAndAuthorize = function (xhr, verb, uri, async, callback) { + var auth, + errorObject; + + if (this.hasClientCredentials()) { + + xhr.open(verb, uri, async); // but see comments below inside the "if userName" + // may have to go with that approach + + if (userName) { + + // set Authorization header + auth = make_basic_auth_header(userName, password); + xhr.setRequestHeader('Authorization', auth); + } + + progress.data.Session._setNoCacheHeaders(xhr); + } else { + // AuthenticationProvider: The AuthenticationProvider is not managing valid credentials. + errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); + } + + callback(errorObject); + }; + }; + + + // Give this constructor the prototype from the "base" AuthenticationProvider + // Do this indirectly by way of an intermediate object so changes to the prototype ("method overrides") + // don't affect other types of AuthenticationProviders that use the prototype) + function BasicProxy() {} + BasicProxy.prototype = progress.data.AuthenticationProvider.prototype; + progress.data.AuthenticationProviderBasic.prototype = new BasicProxy(); + + // Reset the prototype's constructor property so it points to AuthenticationProviderForm rather than + // the one that it just inherited (this is pretty much irrelevant though - the correct constructor + // will get called regardless) + progress.data.AuthenticationProviderBasic.prototype.constructor = + progress.data.AuthenticationProviderBasic; + + + // OVERRIDE METHODS ON PROTOTYPE IF NECESSARY AND POSSIBLE + // (SOME METHODS ARE OVERRIDDEN IN THE CONSTRUCTOR BECAUSE THEY NEED ACCESS TO INSTANCE VARIABLES) + + // NOTE: There are no overrides of the following methods (either here or in the constructor). + // This object uses these methods from the original prototype(i.e., the implementations from the + // AuthenticationProvider object): + // logout (API method) + // hasClientCredentials (API method) + +}()); +//# sourceURL=progress.jsdo.js +/* +progress.auth.form.js Version: 4.4.0-3 + +Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + "use strict"; // note that this makes JSLint complain if you use arguments[x] + + /*global progress : true*/ + /*global $ : false, XMLHttpRequest*/ + + var fn; + + progress.data.AuthenticationProviderForm = function (uri) { + + // PROCESS CONSTRUCTOR ARGUMENTS, CREATE API PROPERTIES, ETC. + this._initialize(uri, progress.data.Session.AUTH_TYPE_FORM, + {"_loginURI": progress.data.AuthenticationProvider._springLoginURIBase, + "_logoutURI": progress.data.AuthenticationProvider._springLogoutURIBase + }); + }; + + // Start by giving this constructor the prototype from the "base" AuthenticationProvider + // Do this indirectly by way of an intermediate object so changes to the prototype ("method overrides") + // don't affect other types of AuthenticationProviders that use the prototype) + function FormProxy() {} + FormProxy.prototype = progress.data.AuthenticationProvider.prototype; + progress.data.AuthenticationProviderForm.prototype = + new FormProxy(); + + // Reset the prototype's constructor property so it points to AuthenticationProviderForm rather than + // the one that it just inherited (this is pretty much irrelevant though - the correct constructor + // will get called regardless) + progress.data.AuthenticationProviderForm.prototype.constructor = + progress.data.AuthenticationProviderForm; + + + // OVERRIDE THE "BASE" AuthenticationProvider PROTOYPE METHODS WHERE NECESSARY + + // All of the methods defined here as part of the AuthenticationProviderForm prototype (instead + // of in the AuthenticationProviderForm constructor) can be inherited by the AuthenticationProviderSSO + // prototype without incurring the overhead of creating a full-blown instance of + // AuthenticationProviderForm to serve as the prototype for the SSO constructor. + // Note: if it turns out that any of the methods defined this way need access to internal variables + // of an AuthenticationProviderForm object, they'll need to be moved out of here. + + // NOTE: There are no overrides of the following methods (either here or in the constructor). + // This object uses these methods from the original prototype(i.e., the implementations from the + // AuthenticationProvider object): + // _reset (general-purpose helper) + // hasClientCredentials (API method) + // _processLoginResult (API helper method) + + + // login API METHOD AND "HELPERS" + progress.data.AuthenticationProviderForm.prototype.login = function (userNameParam, passwordParam) { + var deferred = $.Deferred(), + xhr, + that = this; + + // these throw if the check fails (may want to do something more elegant) + this._checkStringArg("login", userNameParam, 1, "userName"); + this._checkStringArg("login", passwordParam, 2, "password"); + + return this._loginProto("j_username=" + encodeURIComponent(userNameParam) + + "&j_password=" + encodeURIComponent(passwordParam) + "&submit=Submit"); + }; + + // login helper + // Override the protoype's method (this method does not invoke the prototype's copy) + // By defining this here, we can have the SSO AuthenticationProvider use it without + // incurring the overhead of creating a Form instance as the prototype for the SSO constructor + progress.data.AuthenticationProviderForm.prototype._openLoginRequest = function (xhr, uri) { + + xhr.open('POST', uri, true); + + xhr.setRequestHeader("Cache-Control", "max-age=0"); + xhr.setRequestHeader("Pragma", "no-cache"); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + + xhr.withCredentials = true; + + }; + + // logout API METHOD AND "HELPERS" + // Override the prototype method and do not call it because the Anonymous AuthenticationProvider + // doesn't make a server call for logout + // (But this method does do what the SSO AuthenticationProvider needs, so keep it on + // the Form prototype if possible) + progress.data.AuthenticationProviderForm.prototype.logout = function () { + var deferred = $.Deferred(), + xhr, + that = this; + + if (!this._loggedIn) { + // logout is regarded as a success if the AuthenticationProvider isn't logged in + deferred.resolve(this, progress.data.Session.SUCCESS, {}); + } else { + xhr = new XMLHttpRequest(); + this._openLogoutRequest(xhr); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // process the response from the Web application + that._processLogoutResult(xhr, deferred); + } + }; + + xhr.send(); + } + + // Unconditionally reset --- even if the actual server request fails, we still want + // to reset this AuthenticationProvider so it can try a login if desired. + // We also reset even in the case where we're not logged in, just in case. + // (In the future we can add a parameter that controls whether the reinit is unconditional, + // if the developer wants to log out of the token server session but contnue to use the token) + this._reset(); + return deferred.promise(); + }; + + // logout helper (there is no version defined in the original protoype) + progress.data.AuthenticationProviderForm.prototype._openLogoutRequest = function (xhr) { + xhr.open('GET', this._logoutURI, true); + xhr.setRequestHeader("Cache-Control", "max-age=0"); + xhr.withCredentials = true; + xhr.setRequestHeader("Accept", "application/json"); + }; + + // logout helper (there is no version defined in the original protoype) + progress.data.AuthenticationProviderForm.prototype._processLogoutResult = function (xhr, deferred) { + var result; + + if (xhr.status === 200) { + result = progress.data.Session.SUCCESS; + } else if (xhr.status === 401) { + // treat this as a success because the most likely cause is that the session expired + // (Note that an 11.7 OE PAS Web application will return a 200 if we log out with + // an expired JSESSIONID, so this code may not be executed anyway) + result = progress.data.Session.SUCCESS; + } else { + result = progress.data.Session.GENERAL_FAILURE; + } + + this._settlePromise(deferred, result, {"xhr": xhr}); + + }; + + // GENERAL PURPOSE METHOD FOR OPENING REQUESTS (MAINLY FOR JSDO CALLS) + // Override the protoype's method but call it from within the override + // Since the override is being put into the constructor's prototype, and + // since it calls the overridden method which had originally been in the prototype, + // we add a "_super" property to the overriding method so it can still access the original method + // (We could just call that method directly in here like this: + // progress.data.AuthenticationProviderProto.prototype._openRequestAndAuthorize + // but if we ever change the place where we get the initial protoype for + // AuthenticationProviderForm from, we would need to remember to change that here. + // The use of the _super property will handle that automatically, plus it was more fun + // to do it this way) + // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change + // it to use promises + fn = progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize; + progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize = + function (xhr, verb, uri, async, callback) { + + function afterSuper(errorObject) { + xhr.withCredentials = true; + callback(errorObject); + } + + try { + progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize._super.apply( + this, + [xhr, verb, uri, async, afterSuper] + ); + } catch (e) { + callback(e); + } + }; + progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize._super = fn; + + +}()); +//# sourceURL=progress.jsdo.js +/* +progress.auth.sso.js Version: 4.4.0-3 + +Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + "use strict"; // note that this makes JSLint complain if you use arguments[x] + + /*global progress : true*/ + /*global $ : false */ + + var fn; + +// ADD AN OPTIONS PARAM THAT CAN INCLUDE A NAME FOR PAGE REFRESH? + progress.data.AuthenticationProviderSSO = function (uri) { + var that = this, + // SSO specific + _automaticTokenRefresh, + temp, + ssoTokenInfo = null, + tokenDataKeys = { // SSO specific + token: ".access_token", + refreshToken: ".refresh_token", + tokenType: ".token_type", + expiration: ".expires_in", + accessTokenExpiration: ".accessTokenExpiration" + }; + + // PRIVATE FUNCTIONS + // (The constructor uses local variables and functions mainly to try to protect the token + // information as much as possible. A few could probably be defined as properties/methods, but + // there's currently no need for that because the AuthenticationProvider API has no objects + // that inherit from AuthenticationProviderSSO.) + + // Store the given token with the uri as the key. setItem() throws + // a "QuotaExceededError" error if there is insufficient storage space or + // "the user has disabled storage for the site" (Web storage spec at WHATWG) + function storeTokenInfo(info) { + var date, + accessTokenExpiration; + + if (info.access_token.length) { + that._storage.setItem(tokenDataKeys.token, JSON.stringify(info.access_token)); + } + if (info.refresh_token.length) { + that._storage.setItem(tokenDataKeys.refreshToken, JSON.stringify(info.refresh_token)); + // The time given for the access token's expiration is in seconds. We transform it + // into milliseconds and add it to date.getTime() for a more standard format. + date = new Date(); + // This should probably be renamed accessTokenRefreshThreshold + accessTokenExpiration = date.getTime() + (info.expires_in * 1000 * 0.75); + that._storage.setItem(tokenDataKeys.accessTokenExpiration, JSON.stringify(accessTokenExpiration)); + } else { + // if there is no refresh token, remove any existing one. This handles the case where + // we got a new token via refresh, but now we're not being given any more refresh tokens + that._storage.removeItem(tokenDataKeys.refreshToken); + that._storage.removeItem(tokenDataKeys.accessTokenExpiration); + } + that._storage.setItem(tokenDataKeys.tokenType, JSON.stringify(info.token_type)); + that._storage.setItem(tokenDataKeys.expiration, JSON.stringify(info.expires_in)); + } + + // get one of the pieces of data related to tokens from storage (could be the token itself, or + // the refresh token, expiration info, etc.). Returns null if the item isn't in storage + function retrieveTokenProperty(propName) { + var jsonStr = that._storage.getItem(propName), + value = null; + + if (jsonStr !== null) { + try { + value = JSON.parse(jsonStr); + } catch (e) { + value = null; + } + } + return value; + } + + function retrieveToken() { + return retrieveTokenProperty(tokenDataKeys.token); + } + + function retrieveRefreshToken() { + return retrieveTokenProperty(tokenDataKeys.refreshToken); + } + + function retrieveAccessTokenExpiration() { + return retrieveTokenProperty(tokenDataKeys.accessTokenExpiration); + } + + function retrieveTokenType() { + return retrieveTokenProperty(tokenDataKeys.tokenType); + } + + // This is going to be hardcoded for now. This can very + // possibly change in the future if we decide to expose + // the token to the user. + function getToken() { + return retrieveToken(); + } + + function retrieveExpiration() { + return retrieveTokenProperty(tokenDataKeys.expiration); + } + + function clearTokenInfo(info) { + that._storage.removeItem(tokenDataKeys.token); + that._storage.removeItem(tokenDataKeys.refreshToken); + that._storage.removeItem(tokenDataKeys.tokenType); + that._storage.removeItem(tokenDataKeys.expiration); + that._storage.removeItem(tokenDataKeys.accessTokenExpiration); + } + + // function is SSO specific + function openRefreshRequest(xhr) { + xhr.open('POST', that._refreshURI, true); + xhr.setRequestHeader("Cache-Control", "max-age=0"); + xhr.withCredentials = true; + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.setRequestHeader("Accept", "application/json"); + } + + // function is SSO specific + function processRefreshResult(xhr, deferred) { + var errorObject, + result, + ssoTokenJSON; + + if (xhr.status === 200) { + // get token and store it; if that goes well, resolve the promise, otherwise reject it + try { + ssoTokenInfo = JSON.parse(xhr.responseText); + + if (ssoTokenInfo.access_token) { + storeTokenInfo(ssoTokenInfo); + // got the token info, its access_token has a value, and storeTokenInfo() + // didn't thrown an error, so call this a success + result = progress.data.Session.SUCCESS; + } else { + result = progress.data.Session.GENERAL_FAILURE; + // {1}: Unexpected error calling refresh: {error-string} + // ( No token returned from server) + errorObject = new Error(progress.data._getMsgText( + "jsdoMSG049", + "AuthenticationProvider", + "refresh", + progress.data._getMsgText("jsdoMSG050") + )); + } + } catch (ex) { + result = progress.data.Session.GENERAL_FAILURE; + // {1}: Unexpected error calling refresh: {error-string} + // (error could be thrown from storeTokenInfo when it calls setItem()) + errorObject = new Error(progress.data._getMsgText( + "jsdoMSG049", + "AuthenticationProvider", + "refresh", + ex.message + )); + } + } else if (xhr.status === 401) { + that._reset(); // treat authentication failure as the equivalent of a logout + result = progress.data.Session.AUTHENTICATION_FAILURE; + } else { + result = progress.data.Session.GENERAL_FAILURE; + } + + that._settlePromise(deferred, result, {"xhr": xhr, + "errorObject": errorObject}); // OK if undefined + } + + + this._processLoginResult = function (xhr, deferred) { + var errorObject, + result, + ssoTokenJSON; + + if (xhr.status === 200) { + // Need to set loggedIn now so we can call logout from here if there's an + // error processing the response (e.g., authentication succeeded but we didn't get a + // token for some reason) + this._loggedIn = true; + + // get token and store it; if that goes well, resolve the promise, otherwise reject it + try { + ssoTokenInfo = JSON.parse(xhr.responseText); + + if (ssoTokenInfo.access_token) { + storeTokenInfo(ssoTokenInfo); + // got the token info, its access_token has a value, and storeTokenInfo() + // didn't throw an error, so call this a success + result = progress.data.Session.SUCCESS; + } else { + result = progress.data.Session.GENERAL_FAILURE; + // {1}: Unexpected error calling login: {error-string} + // ( No token returned from server) + errorObject = new Error(progress.data._getMsgText( + "jsdoMSG049", + "AuthenticationProvider", + "login", + progress.data._getMsgText("jsdoMSG050") + )); + } + } catch (ex) { + result = progress.data.Session.GENERAL_FAILURE; + // {1}: Unexpected error calling login: {error-string} + // (error could be thrown from storeTokenInfo when it calls setItem()) + errorObject = new Error(progress.data._getMsgText( + "jsdoMSG049", + "AuthenticationProvider", + "login", + ex.message + )); + } + + // log out if there was an error processing the response so the app can try to log in again + if (result !== progress.data.Session.SUCCESS) { + // call logout, but ignore its outcome -- just tell caller that login failed + this.logout() + .always(function (authProv) { + authProv._settlePromise(deferred, result, {"xhr": xhr, + "errorObject": errorObject}); + }); + return; // so we don't execute the reject below, which could invoke the fail handler + // before we're done with the logout + } + + } else if (xhr.status === 401) { + result = progress.data.Session.AUTHENTICATION_FAILURE; + } else { + result = progress.data.Session.GENERAL_FAILURE; + } + + this._settlePromise(deferred, result, {"xhr": xhr}); + }; + + + // Override the protoype's method but call it from within the override. (Define the override + // here in the constructor so it has access to the internal function and variable) + this._reset = function () { + progress.data.AuthenticationProviderSSO.prototype._reset.apply(this); + clearTokenInfo(); + ssoTokenInfo = null; + }; + + + // Override the protoype's method but call it from within the override. (Define the override + // here in the constructor so it has access to the internal function getToken() ) + // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change + // it to use promises + this._openRequestAndAuthorize = function (xhr, + verb, + uri, + async, + callback) { + var that = this, + date, + errorObject; + + function afterRefreshCheck(provider, result, info) { + // if refresh failed because of auth failure, we will have gotten rid of the + // token and reset the auth provider + if (result === progress.data.Session.AUTHENTICATION_FAILURE) { + callback(new Error(progress.data._getMsgText("jsdoMSG060"))); + } else { + // We've done the refresh check (and possible refresh) for SSO, now execute + // the base _openRequest... method, which does common things for Form-based + progress.data.AuthenticationProviderSSO.prototype._openRequestAndAuthorize.apply( + that, + [xhr, verb, uri, async, function (errorObject) { + if (!errorObject) { + xhr.setRequestHeader('Authorization', "oecp " + getToken()); + } + callback(errorObject); + }] + ); + } + } + + if (this.hasClientCredentials()) { + // Every token given has an expiration "hint". If the token's lifespan + // is close to or past that limit, then a refresh is done. + // No matter what the outcome of the refresh, keep in mind we always + // send the original request. + date = new Date(); + if (this.automaticTokenRefresh && + this.hasRefreshToken() && + date.getTime() > retrieveAccessTokenExpiration()) { + try { + this.refresh() + .always(function (provider, result, info) { + afterRefreshCheck(provider, result, info); + }); + } catch (e) { + callback(e); + } + } else { + afterRefreshCheck(this, progress.data.Session.SUCCESS, null); + } + } else { + // This message is SSO specific, unless we can come up with a more general message + // JSDOSession: The AuthenticationProvider needs to be managing a valid token. + errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); + callback(errorObject); + } + }; + + + // API METHODS + + // override the prototype's hasClientCredentials method + this.hasClientCredentials = function () { + return (retrieveToken() === null ? false : true); + }; + + + this.refresh = function () { + var deferred = $.Deferred(), + xhr; + + if (!this._loggedIn) { + // "The refresh method was not executed because the AuthenticationProvider is not logged in." + throw new Error(progress.data._getMsgText("jsdoMSG053", "AuthenticationProvider", "refresh")); + } + + if (!this.hasRefreshToken()) { + // "Token refresh was not executed because the AuthenticationProvider does not have a + // refresh token." + throw new Error(progress.data._getMsgText("jsdoMSG054", "AuthenticationProvider")); + } + + xhr = new XMLHttpRequest(); + openRefreshRequest(xhr); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // process the response from the Web application + processRefreshResult(xhr, deferred); + } + }; + + xhr.send('{"token_type":"' + retrieveTokenType() + '","refresh_token":"' + + retrieveRefreshToken() + '"}'); + return deferred.promise(); + }; + + + this.hasRefreshToken = function () { + return (retrieveRefreshToken() === null ? false : true); + }; + + + // PROCESS CONSTRUCTOR ARGUMENTS, CREATE API PROPERTIES, ETC. + this._initialize(uri, + progress.data.Session.AUTH_TYPE_FORM_SSO, + {"_loginURI": progress.data.AuthenticationProvider._springFormTokenLoginURIBase, + "_logoutURI": progress.data.AuthenticationProvider._springLogoutURIBase, + "_refreshURI": progress.data.AuthenticationProvider._springFormTokenRefreshURIBase + }); + + // in addition to the standard AuthenticationProvider properties, an SSO provider also has a property + // to control automatic token refresh (it's enabled by default on the assumption that developers + // will usually want this) + _automaticTokenRefresh = true; + Object.defineProperty(this, 'automaticTokenRefresh', + { + get: function () { + return _automaticTokenRefresh; + }, + set: function (value) { + if (value === true || value === false) { + _automaticTokenRefresh = value; + } else { + throw new Error(progress.data._getMsgText("jsdoMSG061", + "AuthenticationProvider", + "automaticTokenRefresh")); + } + }, + enumerable: true + }); + + // add the automaticTokenRefresh key to the base class's list of data keys + this._dataKeys.automaticTokenRefresh = this._storageKey + ".automaticTokenRefresh"; + // set it from storage, if it's in storage + temp = this._retrieveInfoItem(this._dataKeys.automaticTokenRefresh); + if (temp === false) { + _automaticTokenRefresh = false; + } + + // We're currently storing the token in storage with the + // uri as the key. This is subject to change later. + tokenDataKeys.token = this._storageKey + tokenDataKeys.token; + tokenDataKeys.refreshToken = this._storageKey + tokenDataKeys.refreshToken; + tokenDataKeys.tokenType = this._storageKey + tokenDataKeys.tokenType; + tokenDataKeys.expiration = this._storageKey + tokenDataKeys.expiration; + tokenDataKeys.accessTokenExpiration = this._storageKey + tokenDataKeys.accessTokenExpiration; + + // NOTE: we rely on the prototype's logic to set this._loggedIn. An alternative could be to + // use the presence of a token to determine that, but it's conceivable that we could be + // logged in but for some reason not have a token (e.g., a token expired, or we logged in + // but the authentication server did not return a token) + if (retrieveToken()) { + this._loggedIn = true; + } + + // END OF CONSTRUCTOR PROCESSING + + }; + // END OF AuthenticationProviderSSO CONSTRUCTOR + + // NOTE: This is used only for the SSO authentication. + // Define the prototype as an instance of an AuthenticationProviderForm object + function SSOProxy() {} + SSOProxy.prototype = progress.data.AuthenticationProviderForm.prototype; + progress.data.AuthenticationProviderSSO.prototype = + new SSOProxy(); + + // But reset the constructor back to the SSO constructor (this is pretty much irrelevant, + // though. The correct constructor would be called anyway. It's mainly for the sake of anyone + // wanting to see what the constructor of an object is (maybe a framework) + progress.data.AuthenticationProviderSSO.prototype.constructor = + progress.data.AuthenticationProviderSSO; + + // override the base AuthenticationProvider _storeInfo and _clearinfo, but keep refs so they + // can be invoked within the overrides + fn = progress.data.AuthenticationProviderSSO.prototype._storeInfo; + progress.data.AuthenticationProviderSSO.prototype._storeInfo = + function () { + progress.data.AuthenticationProviderSSO.prototype._storeInfo._super.apply(this); + this._storage.setItem(this._dataKeys.automaticTokenRefresh, + JSON.stringify(this._automaticTokenRefresh)); + }; + progress.data.AuthenticationProviderSSO.prototype._storeInfo._super = fn; + + fn = progress.data.AuthenticationProviderSSO.prototype._clearInfo; + progress.data.AuthenticationProviderSSO.prototype._clearInfo = + function () { + progress.data.AuthenticationProviderSSO.prototype._clearInfo._super.apply(this); + this._storage.removeItem(this._dataKeys.automaticTokenRefresh); + }; + progress.data.AuthenticationProviderSSO.prototype._clearInfo._super = fn; + + + + // NOTE: There are no overrides of the following methods (either here or in the constructor). + // This object uses these methods from the original prototype(i.e., the implementations from the + // Auth...Form object) because for an OE SSO token server, the login/logout model is Form (the + // only difference is the use of a special URI query string in the login (see the call to + // initialize() in the SSO constructor (above)): + // login (API method) + // _openLoginRequest (API helper method) + // logout (API method) + // _openLogoutRequest (API helper method) + // _processLogoutResult (API helper method) + + // NOTE: All overrides are implemented in the constructor (rather than adding them to the prototype) + // because they need access to variables and/or functions that are defined in the constructor + // (in an attempt to protect the token info somewhat) + +}()); + + +/* +progress.data.kendo.js Version: 4.4.0-01 + +Copyright (c) 2015-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +/*global jQuery, kendo, progress*/ +/*jslint nomen: true*/ +/*jslint vars: false*/ +(function () { + + // "use strict"; + + var JSDODataReader, JSDOTransport, JSDOObservable = new kendo.Observable(); + + function initializeJSDO(transport, options) { + var jsdo, resourceName; + + if (options.jsdo instanceof progress.data.JSDO) { + jsdo = options.jsdo; + } else if (typeof (options.jsdo) === "string") { + // Create a new JSDO instance using the specified configuration + resourceName = options.jsdo; + + // Create JSDO + jsdo = new progress.data.JSDO({ name: resourceName }); + } else { + throw new Error("JSDO: jsdo property must be either a JSDO instance or a string."); + } + + if (transport.tableRef === undefined && jsdo._defaultTableRef) { + transport.tableRef = jsdo._defaultTableRef._name; + } + if (transport.tableRef === undefined) { + throw new Error("JSDO: A tableRef must be specified when using a multi-table DataSet."); + } else if (jsdo[transport.tableRef] === undefined) { + throw new Error("JSDO: tableRef '" + transport.tableRef + "' is not present in JSDO definition."); + } + + return jsdo; + } + + + + // This define functions to read the data object from the JSDO DataSource + JSDODataReader = kendo.data.readers.json.extend({ + init: function (arg1) { + // init function + var event = {}, + transport; + + // Query the transport object to obtain a transport reference + JSDOObservable.trigger("info", event); + transport = this.transport = event.sender._events.info.transport; + + kendo.data.readers.json.fn.init.call(this, arg1); + + // Overrides model property after init.call + // because if model is defined at the object level init.call removes it + this.model = kendo.data.Model.define({ + init: function (data) { + var record; + if (!data || jQuery.isEmptyObject(data)) { + data = transport._getInitialValues(); + } + record = transport._convertDataTypes(data); + transport.jsdo._deleteProdsProperties(record, true); + kendo.data.Model.fn.init.call(this, record); + }, + id: "_id", + fields: transport._getModel() + }); + }, + total: function (data) { + return data.total || (data.data ? data.data.length : data.length); + }, + data: function (data) { + return data.data || data; + } + }); + + // This define transport for JSDO DataSource providing implementation for: + // create, read, update, destroy, submit + JSDOTransport = kendo.data.RemoteTransport.extend({ + init: function (options) { + var transport = this, + fnName; + + if (options.tableRef !== undefined) { + transport.tableRef = options.tableRef; + } + transport.jsdo = initializeJSDO(transport, options); + transport._initFromServer = false; + transport.autoSave = options.autoSave !== undefined ? options.autoSave : true; + transport.readLocal = options.readLocal !== undefined ? options.readLocal : false; + transport.countFnName = options.countFnName; + transport.useArrays = options.useArrays !== undefined ? options.useArrays : false; + + if (transport.countFnName !== undefined) { + if (typeof (transport.jsdo[transport.countFnName]) !== "function") { + throw new Error("Invoke operation '" + + transport.countFnName + "' for countFnName is not defined."); + } + } else if (transport.jsdo._resource.generic.count !== undefined) { + for (fnName in transport.jsdo._resource.fn) { + if (transport.jsdo._resource.fn.hasOwnProperty(fnName)) { + if (transport.jsdo._resource.generic.count === transport.jsdo._resource.fn[fnName]["function"]) { + transport.countFnName = fnName; + break; + } + } + } + } + + // Define "info" event to return transport object to reader + JSDOObservable.one("info", function (e) { + e.sender._events.info.transport = transport; + }); + + transport._initConvertTypes(); + + kendo.data.RemoteTransport.fn.init.call(this, options); + }, + _initConvertTypes: function () { + // _initConvertTypes: + // Initializes transport._convertTypes to indicate whether a conversion of the data is needed + // when it is passed to Kendo UI. + // This operation is currently only needed for date fields that are stored as strings. + // Sets array _dateFields to the fields of date fields to convert. + var transport = this, + i, + schema, + fieldName, + dateFields = [], + arrayFields = [], + convertDateFields = false; + + transport._convertTypes = false; + + schema = transport.jsdo[transport.tableRef].getSchema(); + for (i = 0; i < schema.length; i += 1) { + fieldName = schema[i].name; + if (fieldName.length > 0 && fieldName.charAt(0) !== "_") { + if (schema[i].type === "string" && + schema[i].format && + (schema[i].format.indexOf("date") !== -1)) { + dateFields.push(fieldName); + if (!convertDateFields) { + convertDateFields = true; + } + } else if (!transport.useArrays && schema[i].type === "array") { + arrayFields.push(fieldName); + if (!convertDateFields && schema[i].ablType && + schema[i].ablType.indexOf("DATE") === 0) { + convertDateFields = true; + } + } + } + } + + if (dateFields.length > 0 || arrayFields.length > 0) { + transport._convertTypes = true; + // _convertFields: Object containing arrays for each data type to convert + transport._convertFields = {}; + transport._convertFields._arrayFields = []; + transport._convertFields._dateFields = []; + } + if (dateFields.length > 0) { + transport._convertFields._dateFields = dateFields; + } + if (convertDateFields) { + transport._convertFields._datePattern = new RegExp("^([0-9]+)?-([0-9]{2})?-([0-9]{2})?$"); + transport._convertFields._dateTimePattern = new RegExp( + "^([0-9]+)?-([0-9]{2})?-([0-9]{2})?" + + "T([0-9]{2})?:([0-9]{2})?:([0-9]{2})?.([0-9]{3})?$" + ); + } + if (arrayFields.length > 0) { + transport._convertFields._arrayFields = arrayFields; + } + }, + _convertStringToDate: function (data, fieldName, targetFieldName) { + var transport = this, + array, + ablType, + orig; + + if (!targetFieldName) { + targetFieldName = fieldName; + } + // Check if string is -- + array = transport._convertFields._datePattern.exec(data[targetFieldName]) || []; + if (array.length > 0) { + data[targetFieldName] = new Date(parseInt(array[1], 10), + parseInt(array[2], 10) - 1, + parseInt(array[3], 10)); + } else { + ablType = transport.jsdo[transport.tableRef]._fields[fieldName.toLowerCase()].ablType; + if (ablType === "DATETIME") { + array = transport._convertFields._dateTimePattern.exec(data[targetFieldName]) || []; + if (array.length > 0) { + // Convert date to local time zone + data[targetFieldName] = new Date(parseInt(array[1], 10), + parseInt(array[2], 10) - 1, + parseInt(array[3], 10), + parseInt(array[4], 10), + parseInt(array[5], 10), + parseInt(array[6], 10), + parseInt(array[7], 10)); + } + } + + // Check to see if it was converted + if (typeof (data[targetFieldName]) === "string") { + orig = data[targetFieldName]; + try { + data[targetFieldName] = new Date(data[targetFieldName]); + } + catch (e) { + // Conversion to a date object was not successful + data[targetFieldName] = orig; + console.log(msg.getMsgText("jsdoMSG000", + "_convertStringToDate() could not convert to date object: " + orig)); + } + } + } + }, + _convertDataTypes: function (data) { + // _convertDataTypes: + // Converts data types in the specified data record. + // Data record could come from the JSDO or from the Kendo UI DataSource. + // Returns a reference to the record. + // Returns a copy when useArrays is undefined or false. + var transport = this, + i, + k, + fieldName, + schemaInfo, + prefixElement, + elementName, + copy; + + if (!transport.useArrays && transport._convertTypes && (transport._convertFields._arrayFields.length > 0)) { + copy = {}; + transport.jsdo._copyRecord(transport.jsdo._buffers[transport.tableRef], data, copy); + data = copy; + } + + if (!transport._convertTypes) { + return data; + } + + for (k = 0; k < transport._convertFields._arrayFields.length; k += 1) { + fieldName = transport._convertFields._arrayFields[k]; + if (data[fieldName]) { + schemaInfo = transport.jsdo[transport.tableRef]._fields[fieldName.toLowerCase()]; + prefixElement = transport.jsdo._getArrayField(fieldName); + for (i = 0; i < schemaInfo.maxItems; i += 1) { + // ABL arrays are 1-based + elementName = prefixElement.name + (i + 1); + + if (!transport.jsdo[transport.tableRef]._fields[elementName.toLowerCase()]) { + // Skip element if a field with the same name exists + // Extract value from array field into individual field + // Array is removed later + data[elementName] = data[fieldName][i]; + + // Convert string DATE fields to JS DATE + if ((schemaInfo.ablType) && (schemaInfo.ablType.indexOf("DATE") === 0) && (typeof (data[elementName]) === "string")) { + transport._convertStringToDate(data, fieldName, elementName); + } + } + } + if (!transport.useArrays) { + delete data[fieldName]; + } + } + } + + for (k = 0; k < transport._convertFields._dateFields.length; k += 1) { + fieldName = transport._convertFields._dateFields[k]; + if (typeof (data[fieldName]) === "string") { + transport._convertStringToDate(data, fieldName); + } + } + + return data; + }, + _getModel: function () { + var transport = this, + i, + j, + fields = {}, + schema, + value, + type, + element; + + schema = transport.jsdo[transport.tableRef].getSchema(); + for (i = 0; i < schema.length; i += 1) { + // Skip internal fields + if (schema[i].name.length > 0 && schema[i].name.charAt(0) !== "_") { + type = schema[i].type; + if (type === "integer") { + type = "number"; + } else if (type === "string" && + schema[i].format && + schema[i].format.indexOf("date") !== -1) { + // Set type to "date" is type is DATE or DATETIME[-TZ] + type = "date"; + } + if (type === "array") { + for (j = 0; j < schema[i].maxItems; j += 1) { + value = transport.jsdo._getDefaultValue(schema[i]); + element = transport.jsdo._getArrayField(schema[i].name, j); + if (!transport.jsdo[transport.tableRef]._fields[element.name.toLowerCase()]) { + // Skip element if a field with the same name exists + + // Calculate type of array element + type = schema[i].items.type; + if (type === "integer") { + type = "number"; + } else if (type === "string" && schema[i].ablType && (schema[i].ablType.indexOf("DATE") !== -1)) { + type = "date"; + } + + fields[element.name] = {}; + fields[element.name].type = type; + if (value !== undefined) { + fields[element.name].defaultValue = value; + } + } + } + } else { + value = transport.jsdo._getDefaultValue(schema[i]); + fields[schema[i].name] = {}; + fields[schema[i].name].type = type; + if (value !== undefined) { + fields[schema[i].name].defaultValue = value; + } + } + } + } + return fields; + }, + _getInitialValues: function () { + var transport = this, + i, + j, + data = {}, + schema, + defaultValue; + schema = transport.jsdo[transport.tableRef].getSchema(); + for (i = 0; i < schema.length; i += 1) { + // Skip internal fields + if (schema[i].name.length > 0 && schema[i].name.charAt(0) !== "_") { + defaultValue = transport.jsdo._getDefaultValue(schema[i]); + if (schema[i].type === "array") { + data[schema[i].name] = []; + + for (j = 0; j < schema[i].maxItems; j += 1) { + data[schema[i].name][j] = defaultValue; + } + + } else { + data[schema[i].name] = defaultValue; + } + } + } + return data; + }, + _getData: function (options) { + var jsdo = this.jsdo, + data = {}, + params, + array, + filter; + + if (options && options.data) { + params = { + tableRef: this.tableRef, + filter: options.data.filter, + sort: options.data.sort, + skip: options.data.skip, + top: options.data.take + }; + + array = jsdo[this.tableRef].getData({filter: filter}); + data.total = array.length; + array = jsdo[this.tableRef].getData(params); + data.data = array; + } else { + array = jsdo[this.tableRef].getData(); + data.data = array; + data.total = array.length; + } + + return data; + }, + read: function (options) { + try { + var jsdo = this.jsdo, + filter, + data = {}, + transport = this, + callback, + property, + optionsMapping = { + filter: "filter", + take: "top", + skip: "skip", + sort: "sort" + }, + saveUseRelationships; + + if (!this._initFromServer) { + if (jsdo[this.tableRef]._parent) { + this._initFromServer = (jsdo[jsdo[this.tableRef]._parent]._data && (jsdo[jsdo[this.tableRef]._parent]._data.length > 0)) + || (jsdo[this.tableRef]._data instanceof Array && (jsdo[this.tableRef]._data.length > 0)); + } else { + this._initFromServer = (jsdo[this.tableRef]._data instanceof Array) && (jsdo[this.tableRef]._data.length > 0); + } + } + + data.data = []; + if (this.readLocal && this._initFromServer) { + saveUseRelationships = jsdo.useRelationships; + jsdo.useRelationships = false; + data = this._getData(options); + jsdo.useRelationships = saveUseRelationships; + options.success(data); + return; + } + + if (!this.readLocal) { + // readLocal is false or _initFromServer is false + + if (options.data) { + // Only create filter object if options.data contains viable properties + for (property in options.data) { + if (options.data.hasOwnProperty(property)) { + if (options.data[property] !== undefined + && optionsMapping[property]) { + if (filter === undefined) { + filter = {}; + } + filter[optionsMapping[property]] = options.data[property]; + } + } + } + if (filter) { + filter.tableRef = this.tableRef; + } + } + } + + callback = function onAfterFillJSDO(jsdo, success, request) { + var data = {}, saveUseRelationships, promise, total, exception; + + if (success) { + saveUseRelationships = jsdo.useRelationships; + jsdo.useRelationships = false; + + if (transport.readLocal) { + // Use options.data to filter data + data = transport._getData(options); + } else { + data.data = jsdo[transport.tableRef].getData(); + + total = jsdo.getProperty("server.count"); + if (total) { + data.total = total; + } + + } + jsdo.useRelationships = saveUseRelationships; + transport._initFromServer = true; + if (options.data && options.data.take) { + if (!transport.readLocal && + transport.countFnName !== undefined && + typeof (jsdo[transport.countFnName]) === "function") { + + if (options.data.skip === 0 && options.data.take > data.data.length) { + options.success(data); + return; + } + + // Reuse filter string from the request.objParam object from fill() call. + promise = jsdo.invoke( + transport.countFnName, + { filter: request.objParam.filter } + ); + /*jslint unparam: true*/ + promise.done(function (jsdo, success, request) { + var exception, total; + + try { + if (typeof (request.response) === "object" && + Object.keys(request.response).length === 1) { + total = request.response[Object.keys(request.response)]; + if (typeof (total) !== "number") { + // Use generic exception if data type is not a number. + total = undefined; + } + } + } catch (e) { + // This exception is ignored a generic exception is used later. + } + if (total !== undefined) { + if (total) { + data.total = total; + } + options.success(data); + } else { + exception = new Error("Unexpected response from '" + + transport.countFnName + "' operation."); + options.error(request.xhr, request.xhr.status, exception); + } + }); + promise.fail(function (jsdo, success, request) { + var exception; + exception = new Error("Error invoking '" + + transport.countFnName + "' operation."); + options.error(request.xhr, request.xhr.status, exception); + }); + /*jslint unparam: false*/ + } else { + options.success(data); + } + } else { + options.success(data); + } + } else { + exception = new Error("Error while reading records."); + options.error(request.xhr, request.xhr.status, exception); + } + }; + + jsdo.fill(filter).done(callback).fail(callback); + + } catch (e) { + options.error(null, null, e); + } + }, + _processChanges: function (options, request) { + var jsdo = this.jsdo, + transport = this, + array, + i, + jsrecord, + id, + record; + + if (options.batch) { + array = []; + if (options.data.created instanceof Array) { + for (i = 0; i < options.data.created.length; i += 1) { + jsrecord = jsdo[transport.tableRef].findById( + options.data.created[i]._id + ); + if (jsrecord) { + record = transport._convertDataTypes(jsrecord.data); + array.push(record); + } else if (jsdo.autoApplyChanges) { + options.error( + null, + null, + new Error("Created record was not found in memory.") + ); + return; + } + } + } + options.success(array, "create"); + + array = []; + if (options.data.updated instanceof Array) { + for (i = 0; i < options.data.updated.length; i += 1) { + jsrecord = jsdo[transport.tableRef].findById( + options.data.updated[i]._id + ); + if (jsrecord) { + record = transport._convertDataTypes(jsrecord.data); + array.push(record); + } else if (jsdo.autoApplyChanges) { + options.error( + null, + null, + new Error("Updated record not found in memory.") + ); + return; + } + } + } + options.success(array, "update"); + + array = []; + if (options.data.destroyed instanceof Array) { + for (i = 0; i < options.data.destroyed.length; i += 1) { + jsrecord = jsdo[transport.tableRef].findById( + options.data.destroyed[i]._id + ); + if (jsrecord && jsdo.autoApplyChanges) { + options.error( + null, + null, + new Error("Deleted record was found in memory.") + ); + return; + } + } + } + options.success(array, "destroy"); + } else { + if (jsdo._resource.idProperty) { + if (request + && request.batch + && request.batch.operations instanceof Array + && request.batch.operations.length === 1) { + id = request.batch.operations[0].jsrecord.data._id; + } + } else { + id = options.data._id; + } + jsrecord = jsdo[transport.tableRef].findById(id); + if (jsrecord) { + record = transport._convertDataTypes(jsrecord.data); + options.success(record); + } else { + options.success({}); + } + } + }, + _saveChanges: function (options) { + var transport = this, + callback = function onAfterSaveChanges(jsdo, success, request) { + var jsrecord, + xhr, + status, + exception; + + if (success) { + // _id is expected to be set for CUD operations + // Deleted records will not be found and data is expected to be undefined + transport._processChanges(options, request); + } else { + if (request.batch + && request.batch.operations instanceof Array + && request.batch.operations.length === 1) { + xhr = request.batch.operations[0].xhr; + status = request.batch.operations[0].xhr.status; + } else if (request.jsrecords) { + xhr = request.xhr; + status = request.xhr.status; + } + exception = new Error("Error while saving changes."); + options.error(xhr, status, exception); + } + }; + + if (this.autoSave) { + this.jsdo.saveChanges(this.jsdo._hasSubmitOperation).done(callback).fail(callback); + } else { + this._processChanges(options); + } + }, + create: function (options) { + var jsdo = this.jsdo, + jsrecord, + saveUseRelationships = jsdo.useRelationships; + + options.batch = options.data.models instanceof Array; + try { + jsdo.useRelationships = false; + if (options.batch) { + options.error( + null, + null, + new Error("A newer version of Kendo UI is expected for batching support.") + ); + } else { + jsrecord = jsdo[this.tableRef].add(options.data); + options.data._id = jsrecord.data._id; + this._saveChanges(options); + } + } catch (e) { + // Undo changes on thrown exception + if (jsdo.autoApplyChanges) { + jsdo[this.tableRef].rejectChanges(); + } + options.error(null, null, e); + } finally { + jsdo.useRelationships = saveUseRelationships; + } + }, + update: function (options) { + var jsdo = this.jsdo, + jsrecord, + saveUseRelationships = jsdo.useRelationships; + + options.batch = options.data.models instanceof Array; + try { + jsdo.useRelationships = false; + if (options.batch) { + options.error( + null, + null, + new Error("A newer version of Kendo UI is expected for batching support.") + ); + } else { + jsrecord = jsdo[this.tableRef].findById(options.data._id); + jsrecord.assign(options.data); + this._saveChanges(options); + } + } catch (e) { + // Undo changes on thrown exception + if (jsdo.autoApplyChanges) { + jsdo[this.tableRef].rejectChanges(); + } + options.error(null, null, e); + } finally { + jsdo.useRelationships = saveUseRelationships; + } + }, + destroy: function (options) { + var jsdo = this.jsdo, + jsrecord, + saveUseRelationships = jsdo.useRelationships; + + options.batch = options.data.models instanceof Array; + try { + jsdo.useRelationships = false; + if (options.data.models instanceof Array) { + options.error( + null, + null, + new Error("A newer version of Kendo UI is expected for batching support.") + ); + } else { + jsrecord = jsdo[this.tableRef].findById(options.data._id); + jsrecord.remove(); + this._saveChanges(options); + } + } catch (e) { + // Undo changes on thrown exception + if (jsdo.autoApplyChanges) { + jsdo[this.tableRef].rejectChanges(); + } + options.error(null, null, e); + } finally { + jsdo.useRelationships = saveUseRelationships; + } + }, + submit: function (options) { + var jsdo = this.jsdo, + i, + jsrecord, + saveUseRelationships = jsdo.useRelationships; + + options.batch = true; + try { + jsdo.useRelationships = false; + + if (options.data.created instanceof Array) { + for (i = 0; i < options.data.created.length; i += 1) { + jsrecord = jsdo[this.tableRef].add(options.data.created[i]); + options.data.created[i]._id = jsrecord.data._id; + } + } + + if (options.data.updated instanceof Array) { + for (i = 0; i < options.data.updated.length; i += 1) { + jsrecord = jsdo[this.tableRef].findById(options.data.updated[i]._id); + if (jsrecord) { + jsrecord.assign(options.data.updated[i]); + } else { + options.error(null, null, new Error("Record not found in memory.")); + } + } + } + + if (options.data.destroyed instanceof Array) { + for (i = 0; i < options.data.destroyed.length; i += 1) { + jsrecord = jsdo[this.tableRef].findById(options.data.destroyed[i]._id); + if (jsrecord) { + jsrecord.remove(); + } else { + options.error(null, null, new Error("Record not found in memory.")); + } + } + } + + this._saveChanges(options); + } catch (e) { + // Undo changes on thrown exception + if (jsdo.autoApplyChanges) { + jsdo[this.tableRef].rejectChanges(); + } + options.error(null, null, e); + } finally { + jsdo.useRelationships = saveUseRelationships; + } + } + }); + + // This defines the JSDO DataSource by specifying the schema, transport and reader for it. + // The "id" property is set to "_id" to enable CUD operations. + jQuery.extend(true, kendo.data, { + schemas: { + jsdo: { + type: "jsdo", + model: { + id: "_id" + } + } + }, + transports: { + jsdo: JSDOTransport + }, + readers: { + jsdo: JSDODataReader + } + }); +}()); +/*jslint nomen: false*/ From ce900ffc4fa78be7648c837ac86163555b376e9d Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 6 Sep 2023 22:38:07 +0530 Subject: [PATCH 014/135] git ignores --- deploy/.gitignore | 1 + deploy/license/.gitignore | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 deploy/.gitignore create mode 100644 deploy/license/.gitignore diff --git a/deploy/.gitignore b/deploy/.gitignore new file mode 100644 index 0000000..6caf68a --- /dev/null +++ b/deploy/.gitignore @@ -0,0 +1 @@ +output \ No newline at end of file diff --git a/deploy/license/.gitignore b/deploy/license/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/deploy/license/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file From 781d1d194853858cb69fe6864bcfa08cab807633 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 6 Sep 2023 22:39:12 +0530 Subject: [PATCH 015/135] configure DB details --- SportsApp/Sports/conf/startup.pf | 1 + 1 file changed, 1 insertion(+) diff --git a/SportsApp/Sports/conf/startup.pf b/SportsApp/Sports/conf/startup.pf index e69de29..c380391 100644 --- a/SportsApp/Sports/conf/startup.pf +++ b/SportsApp/Sports/conf/startup.pf @@ -0,0 +1 @@ +-db sports -H oedbmachine -S 7654 \ No newline at end of file From b6cc94b4ee671fe7c822d4e06393de3e1acb0287 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 7 Sep 2023 18:24:39 +0530 Subject: [PATCH 016/135] get rid of ant & simplify deploy scripts --- .gitattributes | 6 + .gitignore | 3 + deploy/.gitignore | 3 +- deploy/Dockerfile | 2 +- deploy/README.md | 98 - deploy/ablapps/.gitignore | 4 + deploy/build.xml | 184 - deploy/conf/runtime.properties | 0 deploy/config.properties | 22 - deploy/deploy.sh | 8 + deploy/docker-compose.yml | 56 + deploy/lib/ant-contrib-0.6.jar | Bin 119512 -> 0 bytes deploy/scripts/docker-compose/build.xml | 62 - .../docker-compose.template.yml | 56 - deploy/scripts/docker-compose/readme.txt | 13 - deploy/scripts/docker/build.xml | 111 - deploy/scripts/minikube/build.xml | 106 - .../scripts/minikube/deployment.template.yml | 75 - deploy/scripts/minikube/service.template.yml | 17 - deploy/{ => scripts}/startServer.sh | 12 +- deploy/tasks/common-build-tasks.xml | 86 - deploy/undeploy.sh | 5 + deploy/webui/grid.js | 76 - deploy/webui/index.html | 63 - deploy/webui/progress.all.js | 14881 ---------------- 25 files changed, 89 insertions(+), 15860 deletions(-) create mode 100644 .gitattributes delete mode 100644 deploy/README.md create mode 100644 deploy/ablapps/.gitignore delete mode 100644 deploy/build.xml delete mode 100644 deploy/conf/runtime.properties delete mode 100644 deploy/config.properties create mode 100644 deploy/deploy.sh create mode 100644 deploy/docker-compose.yml delete mode 100644 deploy/lib/ant-contrib-0.6.jar delete mode 100644 deploy/scripts/docker-compose/build.xml delete mode 100644 deploy/scripts/docker-compose/docker-compose.template.yml delete mode 100644 deploy/scripts/docker-compose/readme.txt delete mode 100644 deploy/scripts/docker/build.xml delete mode 100644 deploy/scripts/minikube/build.xml delete mode 100644 deploy/scripts/minikube/deployment.template.yml delete mode 100644 deploy/scripts/minikube/service.template.yml rename deploy/{ => scripts}/startServer.sh (91%) delete mode 100644 deploy/tasks/common-build-tasks.xml create mode 100644 deploy/undeploy.sh delete mode 100644 deploy/webui/grid.js delete mode 100644 deploy/webui/index.html delete mode 100644 deploy/webui/progress.all.js diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..00a51af --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/.gitignore b/.gitignore index 9a6c781..0dbf1d8 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ gradle-app.setting # .project # JDT-specific (Eclipse Java Development Tools) .classpath + +# Ignore Gradle build output directory +build diff --git a/deploy/.gitignore b/deploy/.gitignore index 6caf68a..5629c95 100644 --- a/deploy/.gitignore +++ b/deploy/.gitignore @@ -1 +1,2 @@ -output \ No newline at end of file +output +webui \ No newline at end of file diff --git a/deploy/Dockerfile b/deploy/Dockerfile index 6d5db36..c2410da 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -6,7 +6,7 @@ ARG ROOT_FOLDER=/deploy-staging ARG MANIFEST_VERSION=1.0 # Copy archive file -COPY ./output/package-output/instance ${ROOT_FOLDER}/artifacts/ +COPY ./ablapps/ ${ROOT_FOLDER}/artifacts/ablapps # Create "META-INF/MANIFEST.MF" file RUN mkdir ${ROOT_FOLDER}/META-INF diff --git a/deploy/README.md b/deploy/README.md deleted file mode 100644 index ddb5fec..0000000 --- a/deploy/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# Use the Container Image for PAS for OpenEdge 12.6.0 with a Sample Application - -## Requirements: -* Docker environment -* Docker Compose -* A valid progress.cfg file -* OpenEdge 12.6.0 Environment -* Scripts to deploy Progress Application Server for OpenEdge Container. This can be obtained either from the Progress Download Center or Progress communities. - -Note: For the deployment, we are using service port 8811(sample-app), 9200(elasticsearch), 5601(kibana) and 8080(web-ui). These ports should be available. - -## Use the Container Image - -1. Start a database server if you don't have a running database server. Below is an example - * Create a copy of the sports2000 database and start the database server (broker) - * `prodb sports sports2000` - * `proserve sports -S ` - -2. Build the Sports sample app: - * cd Sports - * For connecting to the database, update the ./conf/startup.pf file with the below content and substituting the required field. - * `-db sports -H -S ` - * In an openedge environment, run the below command - * `ant package` - * Note: A Sports.zip file is generated in ./output/package-output - * Change the working directory to the parent to follow further steps - * `cd ..` - -3. Start the Elasticsearch, Kibana and web-ui service - * Update the value for `serviceURI` in `webui/grid.js` to point to your Docker host. - * Increase virtual memory settings to run Elasticsearch: - * `sudo sysctl -w vm.max_map_count=262144` - * For additional info : https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html - * Start the services - * `docker-compose up -d` - * Check the status of Elasticsearch via a web browser. Elasticsearch may take some time to start. - * `http://:9200` - * Check the Elasticsearch starts: - * `docker-compose logs -f elasticsearch` - -4. Deploy the sample app in PASOE - * Download the PASOE image zip from Electronic Software Distribution (ESD) and unzip it to a folder say `pasoe-sample-app`. - For more details, refer https://docs.progress.com/bundle/pas-for-openedge-docker/ - * Change the directory - * `cd pasoe-sample-app` - * Copy the Sports.zip generated from Step 2 to `./deploy/ablapps` - * `cp ../Sports/output/package-output/Sports.zip ./deploy/ablapps/` - * Copy the config.properties from sample app to `./deploy/config.properties` - * `cp ../config.properties ./deploy/config.properties` - * Update the value for `HOST` in `../fluentbit/conf/fluent-bit-output.conf` to point to your Elasticsearch host. - * Copy the Fluent Bit config from sample app to push the logs to Elasticsearch - * `cp ../fluentbit/conf/fluent-bit-output.conf ./deploy/conf/logging/` - * Copy the license file `progress.cfg` to `./deploy/license` - * If you want to change the database during deployment, you can do this by updating the ./deploy/conf/runtime.properties with below content and substituting the requied field. If `localhost` is used in Step 2 for connecting to the database, please do update the ./deploy/conf/runtime.properties to point to the IP_ADDRESS_OF_DB_HOST. - * `Sports.DB.CONNECTION.PARAMS=-db sports -H -S ` - * Deploy the sample app - * `ant -f ./deploy/build.xml deploy` -5. Access the PAS for OpenEdge instance via a web browser: - * `https://:8811/` - * `https://:8811/Sports/` - * `https://:8811/Sports/static/SportsService.json` - * `https://:8811/Sports/rest/SportsService/Customer` - * Note: By default, the PAS for OpenEdge instance will use HTTPS with a test certificate. You will need to accept access with this certificate. - -6. Access the web-ui service via a web browser: - * `http://:8080` - -7. Access Elasticsearch to check on available logs - * `http://:9200/_cat/indices` - -8. Access Kibana - * `http://:5601` - * Notes: - * Select Management/Index Management to see the indices in Elasticsearch.A index named as `pasoe-container-logs` should be present. - * Select Management/Index Pattern to create an index for Kibana: - * Create index patterns as 'pasoe_container*' - * Specify @timestamp to filter data by time - * Select Discover to see pasoe logs. You can search logs for logtype: - * pasoe_agent_log, pasoe_application_log, pasoe_localhost_log, pasoe_localhost_access_log, start_server_log and etc. - -9. Stop the running services - * Stop Elasticsearch, Kibana, and web-ui - * `docker-compose -f ../docker-compose.yaml down` - * Stop deployed sample app - * `ant -f ./deploy/build.xml undeploy` - - - - - ----- Changes ----- -1. Update readme to not point to OE version 12.6.0 -2. Update build.properties to not have hard coded DLC value - a. update the steps in readme to set DLC -3. Use Gradle version 7.3.3 in `progradle` utility - -Steps -1. Set DLC path \ No newline at end of file diff --git a/deploy/ablapps/.gitignore b/deploy/ablapps/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/deploy/ablapps/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/deploy/build.xml b/deploy/build.xml deleted file mode 100644 index f6bcfbf..0000000 --- a/deploy/build.xml +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Invalid runtime configuration inside ${RUNTIME.PROPERTIES.FILE}: '@{abl.app.name}.DB.CONNECTION.PARAMS=${runtimeProperties.@{abl.app.name}.DB.CONNECTION.PARAMS}'${line.separator}REASON: The ablapp archive is not provided: ${ABL.APP.ARCHIVE} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/deploy/conf/runtime.properties b/deploy/conf/runtime.properties deleted file mode 100644 index e69de29..0000000 diff --git a/deploy/config.properties b/deploy/config.properties deleted file mode 100644 index 91fa336..0000000 --- a/deploy/config.properties +++ /dev/null @@ -1,22 +0,0 @@ -# Deployment mode can be one of: docker/docker-compose/minikube -DEPLOYMENT.MODE=docker-compose - -# Name and tag with which app container image will be built -# Same name will be used as APP_NAME for fluentbit logging -APP.DOCKER.IMAGE.NAME=sports -APP.DOCKER.IMAGE.TAG=latest - -# Container image which contains JDK(compatible) in it -JDK.DOCKER.IMAGE.NAME=eclipse-temurin -JDK.DOCKER.IMAGE.TAG=17.0.3_7-jdk-centos7 -# Location/Path to JDK inside container -JDK.DOCKER.IMAGE.JAVA.LOCATION=/opt/java/openjdk - -PAS.INSTANCE.NAME=oepas1 -PASOE.DOCKER.IMAGE.NAME=progresssoftware/prgs-pasoe -PASOE.DOCKER.IMAGE.TAG=12.8.0 -# In case of kubernetes provide port should be in the default nodePort range: 30000-32767 -PASOE.HTTPS.PORT=8811 - -# Flag to enable fluent-bit logging, defaults to 'true' -FLUENTBIT.LOGGING=true diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100644 index 0000000..41bee6e --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# create the app image +docker build --no-cache -t sports:latest . + +PAS_INSTANCE_NAME=oepas1 +docker-compose -p ${PAS_INSTANCE_NAME} up -d +echo "PASOE instance named '${PAS_INSTANCE_NAME}_dc' will be available at 'https://localhost:8811'" \ No newline at end of file diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..9478212 --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,56 @@ +version: "3.6" +services: + oedbmachine: + image: oedb:latest + ports: + - "7654:7654" + - "7664-7684:7664-7684" + environment: + - DB_BROKER_PORT=7654 + - DB_MINPORT=7664 + - DB_MAXPORT=7684 + web: + image: nginx + ports: + - "8080:80" + volumes: + - ./../SportsApp/webui:/usr/share/nginx/html:ro + jdk: + image: eclipse-temurin:17.0.3_7-jdk-centos7 + volumes: + - jdk_dc:/opt/java/openjdk + ablapp: + image: sports:latest + volumes: + - app_dc:/deploy-staging/artifacts + pasoeinstance: + image: progresssoftware/prgs-pasoe:12.8.0 + depends_on: + - jdk + - ablapp + environment: + - FLUENTBIT_LOGGING=true + - APP_NAME=sports + - INSTANCE_NAME=oepas1 + ports: + - "8811:8811" + container_name: "oepas1_pasoeinstance_dc" + command: ["/bin/sh", "-c", "sh /deploy/scripts/startServer.sh"] + volumes: + - type: volume + source: jdk_dc + target: /usr/java + volume: + nocopy: true + - type: volume + source: app_dc + target: /deploy/artifacts + volume: + nocopy: true + - ./license/progress.cfg:/psc/dlc/progress.cfg + - ./conf/runtime.properties:/deploy/scripts/config/runtime.properties + - ./conf/logging:/fluentbit-tlr + - ./scripts/startServer.sh:/deploy/scripts/startServer.sh +volumes: + jdk_dc: + app_dc: diff --git a/deploy/lib/ant-contrib-0.6.jar b/deploy/lib/ant-contrib-0.6.jar deleted file mode 100644 index db90b0aae85e0aba2dc79a75bec216ce678b450c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119512 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/deploy/scripts/docker-compose/docker-compose.template.yml b/deploy/scripts/docker-compose/docker-compose.template.yml deleted file mode 100644 index e1be919..0000000 --- a/deploy/scripts/docker-compose/docker-compose.template.yml +++ /dev/null @@ -1,56 +0,0 @@ -version: "3.6" -services: - oedbmachine: - image: oedb:latest - ports: - - "7654:7654" - - "7664-7684:7664-7684" - environment: - - DB_BROKER_PORT=7654 - - DB_MINPORT=7664 - - DB_MAXPORT=7684 - web: - image: nginx - ports: - - "8080:80" - volumes: - - ${WEB.UI.DIR}:/usr/share/nginx/html:ro - jdk: - image: ${JDK.DOCKER.IMAGE.FULLNAME} - volumes: - - ${JDK.VOLUME}:${JDK.DOCKER.IMAGE.JAVA.LOCATION} - ablapp: - image: ${APP.DOCKER.IMAGE.FULLNAME} - volumes: - - ${APP.VOLUME}:/deploy-staging/artifacts - pasoeinstance: - image: ${PASOE.DOCKER.IMAGE.FULLNAME} - depends_on: - - jdk - - ablapp - environment: - - FLUENTBIT_LOGGING=${FLUENTBIT.LOGGING} - - APP_NAME=${APP.NAME} - - INSTANCE_NAME=${PAS.INSTANCE.NAME} - ports: - - "${PASOE.HTTPS.PORT}:8811" - container_name: "${PAS.INSTANCE.NAME}_pasoeinstance_dc" - command: ["/bin/sh", "-c", "sh /deploy/scripts/startServer.sh"] - volumes: - - type: volume - source: ${JDK.VOLUME} - target: /usr/java - volume: - nocopy: true - - type: volume - source: ${APP.VOLUME} - target: /deploy/artifacts - volume: - nocopy: true - - ${LICENSE.FILE}:/psc/dlc/progress.cfg - - ${RUNTIME.CONFIG}:/deploy/scripts/config/runtime.properties - - ${LOGGING.CONF.DIR}:/fluentbit-tlr - - /mnt/c/Users/rahulk/work/pug2023/pasoe-docker/deployScripts/deploy/startServer.sh:/deploy/scripts/startServer.sh -volumes: - ${JDK.VOLUME}: - ${APP.VOLUME}: diff --git a/deploy/scripts/docker-compose/readme.txt b/deploy/scripts/docker-compose/readme.txt deleted file mode 100644 index f312e9d..0000000 --- a/deploy/scripts/docker-compose/readme.txt +++ /dev/null @@ -1,13 +0,0 @@ -## Requirements ## -- Docker Engine version: 18.02.0+ -- JDK image - -## Configuration ## -Provide details of JDK image in the docker-compose.yaml file - -## Deploy ## -- docker-compose build -- docker-compose up -d - -## Undeploy ## -- docker-compose down -v \ No newline at end of file diff --git a/deploy/scripts/docker/build.xml b/deploy/scripts/docker/build.xml deleted file mode 100644 index b44b4eb..0000000 --- a/deploy/scripts/docker/build.xml +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/deploy/scripts/minikube/build.xml b/deploy/scripts/minikube/build.xml deleted file mode 100644 index bd1b252..0000000 --- a/deploy/scripts/minikube/build.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/deploy/scripts/minikube/deployment.template.yml b/deploy/scripts/minikube/deployment.template.yml deleted file mode 100644 index ab2eaee..0000000 --- a/deploy/scripts/minikube/deployment.template.yml +++ /dev/null @@ -1,75 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ${PAS.INSTANCE.NAME} - labels: - app: ${PAS.INSTANCE.NAME} - version: v1 -spec: - selector: - matchLabels: - app: ${PAS.INSTANCE.NAME} - version: v1 - template: - metadata: - labels: - app: ${PAS.INSTANCE.NAME} - version: v1 - spec: - volumes: - - name: deploy-artifacts-dir - emptyDir: {} - - name: java-dir - emptyDir: {} - - name: license-dir - secret: - secretName: progress-122-license - - name: runtime-config-${PAS.INSTANCE.NAME} - configMap: - name: runtime-config-${PAS.INSTANCE.NAME} - - name: logging-config-${PAS.INSTANCE.NAME} - configMap: - name: logging-config-${PAS.INSTANCE.NAME} - initContainers: - - image: "${APP.DOCKER.IMAGE.FULLNAME}" - name: war - imagePullPolicy: IfNotPresent - command: ["/bin/sh", "-c", "cp -r /deploy-staging/artifacts/* /deploy/artifacts"] - volumeMounts: - - mountPath: /deploy/artifacts - name: deploy-artifacts-dir - - image: "${JDK.DOCKER.IMAGE.FULLNAME}" - name: copy-java - imagePullPolicy: IfNotPresent - command: ["/bin/sh", "-c", "cp -r ${JDK.DOCKER.IMAGE.JAVA.LOCATION}/* /java"] - volumeMounts: - - name: java-dir - mountPath: /java - containers: - - name: ${PAS.INSTANCE.NAME}-container - image: "${PASOE.DOCKER.IMAGE.FULLNAME}" - imagePullPolicy: IfNotPresent - command: ["/bin/sh", "-c", "cp /cfg/progress.cfg /psc/dlc/progress.cfg && sh /deploy/scripts/startServer.sh"] - volumeMounts: - - name: deploy-artifacts-dir - mountPath: /deploy/artifacts - - name: java-dir - mountPath: /usr/java - - name: license-dir - readOnly: false - mountPath: /cfg - - name: runtime-config-${PAS.INSTANCE.NAME} - mountPath: /deploy/scripts/config - - name: logging-config-${PAS.INSTANCE.NAME} - mountPath: /fluentbit-tlr - env: - - name: FLUENTBIT_LOGGING - value: "${FLUENTBIT.LOGGING}" - - name: INSTANCE_NAME - value: ${PAS.INSTANCE.NAME} - - name: APP_NAME - value: ${APP.NAME} - ports: - - name: https - containerPort: 8811 - protocol: TCP diff --git a/deploy/scripts/minikube/service.template.yml b/deploy/scripts/minikube/service.template.yml deleted file mode 100644 index 2e71522..0000000 --- a/deploy/scripts/minikube/service.template.yml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: ${PAS.INSTANCE.NAME}-service - labels: - app: ${PAS.INSTANCE.NAME} - version: v1 -spec: - type: NodePort - ports: - - targetPort: https - nodePort: ${PASOE.HTTPS.PORT} - port: 8811 - protocol: TCP - selector: - app: ${PAS.INSTANCE.NAME} - version: v1 diff --git a/deploy/startServer.sh b/deploy/scripts/startServer.sh similarity index 91% rename from deploy/startServer.sh rename to deploy/scripts/startServer.sh index 0f61819..000bfcb 100644 --- a/deploy/startServer.sh +++ b/deploy/scripts/startServer.sh @@ -57,21 +57,17 @@ elif [ ! -x "${JAVA_HOME}/bin/java" ]; then echo "JAVA not found at ${JAVA_HOME} # Validate if INSTANCE_NAME is set if [ -z ${INSTANCE_NAME} ]; then echo "INSTANCE_NAME is not set"; exit 1; fi -# Validate if .zip is provided -if [ ! -s "/deploy/artifacts/${INSTANCE_NAME}.tar.gz" ]; then echo "'${INSTANCE_NAME}.tar.gz' is not provided at '/deploy/artifacts'"; exit 1; fi +# Validate if ABL App archive is provided +if [ ! -s "/deploy/artifacts/ablapps/Sports.oear" ]; then echo "'Sports.oear' is not provided at '/deploy/artifacts/ablapps'"; exit 1; fi ## Create directory for additional log files LOG_PATH=/psc/wrk/logs mkdir -p ${LOG_PATH} -## extract instance archive -cd ${WRKDIR} -mkdir -p tmp -tar -xzf /deploy/artifacts/${INSTANCE_NAME}.tar.gz -C tmp - ## Start PASOE server +cd ${WRKDIR} ${DLC}/bin/pasman create -v -Z pas -u admin:admin ${INSTANCE_NAME} -${INSTANCE_NAME}/bin/tcman.sh import -v tmp/ablapps/Sports.oear +${INSTANCE_NAME}/bin/tcman.sh import -v /deploy/artifacts/ablapps/Sports.oear ${INSTANCE_NAME}/bin/tcman.sh start cd /deploy/scripts diff --git a/deploy/tasks/common-build-tasks.xml b/deploy/tasks/common-build-tasks.xml deleted file mode 100644 index 3dc48c1..0000000 --- a/deploy/tasks/common-build-tasks.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - project.setProperty(attributes.get("to"), attributes.get("string").toLowerCase()); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/deploy/undeploy.sh b/deploy/undeploy.sh new file mode 100644 index 0000000..f3fdf60 --- /dev/null +++ b/deploy/undeploy.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +PAS_INSTANCE_NAME=oepas1 +docker-compose -p ${PAS_INSTANCE_NAME} down -v +echo "Removed '${PAS_INSTANCE_NAME}_dc'" \ No newline at end of file diff --git a/deploy/webui/grid.js b/deploy/webui/grid.js deleted file mode 100644 index 04b5078..0000000 --- a/deploy/webui/grid.js +++ /dev/null @@ -1,76 +0,0 @@ -/*global alert, $, progress, require */ - -// require("@progress/jsdo"); - -$(function () { - 'use strict'; - var serviceURI = "https://:8811/Sports"; - var catalogURI = serviceURI + "/static/SportsService.json"; - - function createGrid() { - console.log("DEBUG: createGrid(): "); - $('#grid').kendoGrid({ - dataSource: { - serverFiltering: false, - serverSorting: false, - serverPaging: false, - type: "jsdo", - transport: { - jsdo: "Customer" - }, - error: function (e) { - var messages = ""; - if (e.errorThrown) { - messages += e.errorThrown.message + "\n"; - } - e.sender.transport.jsdo.getErrors().forEach(function (err) { - messages += err.error + "\n"; - }); - alert("Error: \n" + messages); - } - }, - selectable: "multiple row", - navigatable: true, - filterable: true, - height: 400, - groupable: true, - reorderable: true, - resizable: true, - sortable: true, - pageable: { - refresh: true, - pageSizes: true, - pageSize: 10, - buttonCount: 5 - }, - editable: 'inline', - toolbar: ['create'], - columns: [ - { - field: 'CustNum', - title: 'Cust Num', - width: 100 - }, - {field: 'Name'}, - {field: 'State'}, - {field: 'Country'}, - {command: ['edit', 'destroy'], title: ' ', width: '250px'} - ] - }); - } - - try { - // Create a new session object - progress.data.getSession({ - serviceURI: serviceURI, - catalogURI: catalogURI, - authenticationModel: "anonymous" - }).then(function (/* jsdosession, result, info */) { - createGrid(); - }, function (/* jsdosession, result, info */) { - alert("Error while creating session."); - }); - } catch (e) { - alert("Error instantiating objects: " + e); - } -}); diff --git a/deploy/webui/index.html b/deploy/webui/index.html deleted file mode 100644 index 7eee979..0000000 --- a/deploy/webui/index.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - PUG Challenge 2023! - - - - - - - - - - - - - - -

-

Welcome to PUG Challenge 2023!

-
- -
-

SportsApp

-

Customer Management

-
-
- -
-
-

Powered by © Progress Software Corporation

-
-
- - - \ No newline at end of file diff --git a/deploy/webui/progress.all.js b/deploy/webui/progress.all.js deleted file mode 100644 index 5082570..0000000 --- a/deploy/webui/progress.all.js +++ /dev/null @@ -1,14881 +0,0 @@ -/* -Progress JSDO Version: 4.4.1 - -Copyright 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. -*/ -/* -progress.util.js Version: 4.4.0-7 - -Copyright (c) 2014-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -Contains support objects used by the jsdo and/or session object - -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. - - */ -/*global progress:true*/ -/*jslint nomen: true*/ -(function () { - - /* Define these if not defined yet - they may already be defined if - * progress.js was included first */ - if (typeof progress === "undefined") { - progress = {}; - } - - if (typeof progress.data === "undefined") { - progress.data = {}; - } - - progress.util = {}; - - var STRING_OBJECT_TYPE = "String", - DATE_OBJECT_TYPE = "Date", - CHARACTER_ABL_TYPE = "CHARACTER"; - - - /** - * Utility class that allows subscribing and unsubscribing from named events. - * - * @returns {progress.util.Observable} - */ - progress.util.Observable = function () { - /* - * Example format of the events object. Some event delegates may only - * have a function setup, others may optionally have scope, and possibly an operation filter - * - * var events = { - * afterfill : [{ - * scope : {}, // this is optional - * fn : function () {}, - * operation : 'getCustomers' // this is optional - * }, ...] - * - * } - * - * - * - */ - - /* - * remove the given function from the array of observers - */ - function _filterObservers(observers, fn, scope, operation) { - return observers.filter(function (el) { - if (el.fn !== fn || el.scope !== scope || el.operation !== operation) { - return el; - } - }, this); - } - - /* - * validate the arguments passed to the subscribe function - */ - this.validateSubscribe = function (args, evt, listenerData) { - - if (args.length >= 2 && (typeof args[0] === 'string') && (typeof args[1] === 'string')) { - listenerData.operation = args[1]; - listenerData.fn = args[2]; - listenerData.scope = args[3]; - - } else if (args.length >= 2 && (typeof args[0] === 'string') && (typeof args[1] === 'function')) { - listenerData.operation = undefined; - listenerData.scope = args[2]; - listenerData.fn = args[1]; - } else { - throw new Error(); - } - - }; - - - /* - * bind the specified function so it receives callbacks when the - * specified event name is called. Event name is not case sensitive. - * An optional scope can be provided so that the function is executed - * in the given scope. If no scope is given, then the function will be - * called without scope. - * - * If the same function is registered for the same event a second time with - * the same scope the original subscription is removed and replaced with the new function - * to be called in the new scope. - * - * This method has two signatures. - * - * Signature 1: - * @param evt The name of the event to bind a handler to. String. Not case sensitive. - * @param fn The function callback for the event . Function. - * @param scope The scope the function is to be run in. Object. Optional. - * - * Signature 2: - * - * @param evt The name of the event to bind a handler to. String. Not case sensitive - * @param operation The name of the operation to bind to. String. Case sensitive. - * @param fn The function callback for the event . Function. - * @param scope The scope the function is to be run in. Object. Optional. - - */ - this.subscribe = function (evt, operation, fn, scope) { - var listenerData, - observers; - - if (!evt) { - throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "subscribe")); - } - - if (typeof evt !== 'string') { - throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), - "subscribe", progress.data._getMsgText("jsdoMSG039"))); - } - - this._events = this._events || {}; - evt = evt.toLowerCase(); - listenerData = {fn: undefined, scope: undefined, operation: undefined}; - - try { - this.validateSubscribe(arguments, evt, listenerData); - } catch (e) { - throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), - "subscribe", e.message)); - } - - observers = this._events[evt] || []; - - // make sure we don't add duplicates - observers = _filterObservers(observers, listenerData.fn, - listenerData.scope, listenerData.operation); - observers.push(listenerData); - this._events[evt] = observers; - - return this; - }; - - /* - * remove the specified function so it no longer receives events from - * the given name. event name is not case sensitive. - * - * This method has two signaturues. - * Signature 1: - * @param evt Required. The name of the event for which to unbind the given function. String. - * @param fn Required. The function to remove from the named event. Function. - * @param scope Optional. The function scope in which to remove the listener. Object. - * - * Signature 2: - * - * @param evt Required. The name of the event for which to unbind the given function. - String. Not case sensitive - * @param operation Required. The name of the operation to receive events. String. Case Sensitive - * @param fn Required. The function to remove from the named event. Function. - * @param scope Optional. The function scope in which to remove the listener. Object. - * - */ - this.unsubscribe = function (evt, operation, fn, scope) { - var listenerData, - observers; - - if (!evt) { - throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "unsubscribe")); - } - - if (typeof evt !== 'string') { - throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), - "unsubscribe", progress.data._getMsgText("jsdoMSG037"))); - } - - this._events = this._events || {}; - evt = evt.toLowerCase(); - listenerData = {fn: undefined, scope: undefined, operation: undefined}; - try { - this.validateSubscribe(arguments, evt, listenerData); - } catch (e) { - // throw new Error("Invalid signature for unsubscribe. " + e.message); - throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), - "unsubscribe", e.message)); - } - - observers = this._events[evt] || []; - if (observers.length > 0) { - this._events[evt] = _filterObservers(observers, listenerData.fn, - listenerData.scope, listenerData.operation); - } - - return this; - }; - - /* - * trigger an event of the given name, and pass the specified data to - * the subscribers of the event. Event name is not case sensitive. - * A variable numbers of arguments can be passed as arguments to the event handler. - * - * This method has two signatures - * Signature 1: - * @param evt The name of the event to fire. String. Not case sensitive. - * @param operation The name of the operation. String. Case sensitive - * @param args Optional. A variable number of arguments to pass to the event handlers. - * - * Signature 2: - * @param evt The name of the event to fire. String. Not case sensitive - * @param args Optional. A variable number of arguments to pass to the event handlers. - */ - this.trigger = function (evt, operation, args) { - var observers, - op; - - if (!evt) { - throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "trigger")); - } - - this._events = this._events || {}; - evt = evt.toLowerCase(); - observers = this._events[evt] || []; - if (observers.length > 0) { - args = Array.prototype.slice.call(arguments); - - if ((arguments.length >= 2) - && (typeof evt === 'string') - && (typeof operation === 'string')) { - // in alt format the second argument is the event name, - // and the first is the operation name - op = operation; - args = args.length > 2 ? args.slice(2) : []; - } else if (arguments.length >= 1 && (typeof evt === 'string')) { - op = undefined; - args = args.length > 1 ? args.slice(1) : []; - } else { - throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), "trigger")); - } - - observers.forEach(function (el) { - if (el.operation === op) { - el.fn.apply(el.scope, args); - } - }); - - } - - return this; - }; - - // unbind all listeners from the given event. If the - // evt is undefined, then all listeners for all events are unbound - // evnt name is not case sensitive - // @param evt Optional. The name of the event to unbind. If not passed, then all events are unbound - this.unsubscribeAll = function (evt, operation) { - var observers; - - if (evt) { - this._events = this._events || {}; - if (typeof evt === 'string') { - evt = evt.toLowerCase(); - observers = this._events[evt] || []; - - observers.forEach(function (el) { - if (el.operation) { - this.unsubscribe(evt, el.operation, el.fn, el.scope); - } else { - this.unsubscribe(evt, el.fn, el.scope); - } - }, this); - } - } else { - this._events = {}; - } - - return this; - }; - }; - - - /** - * Utility class that saves/reads data to localStorage - * - * @returns {progress.data.LocalStorage} - */ - progress.data.LocalStorage = function LocalStorage() { - - /*global localStorage */ - if (typeof localStorage === "undefined") { - // "progress.data.LocalStorage: No support for localStorage." - throw new Error(progress.data._getMsgText("jsdoMSG126", "progress.data.LocalStorage", "localStorage")); - } - - - // "Methods" - - this.saveToLocalStorage = function (name, dataObj) { - localStorage.setItem(name, JSON.stringify(dataObj)); - }; - - this.readFromLocalStorage = function (name) { - - var jsonStr = localStorage.getItem(name), - dataObj = null; - - if (jsonStr !== null) { - try { - dataObj = JSON.parse(jsonStr); - } catch (e) { - dataObj = null; - } - } - return dataObj; - }; - - this.clearLocalStorage = function (name) { - localStorage.removeItem(name); - }; - - }; // End of LocalStorage - - - ///////////////////////////////////////////////////////////////////////////////////////// - // Utility Functions - - /* - * Converts the specified filter object to an OpenEdge ABL Where String. - * - * @param tableRef - handle to the table in jsdo, where string is applied to. - * @param filter - the filter object to convert. - * - * @returns - translated OE where string. - */ - progress.util._convertToABLWhereString = function (tableRef, filter) { - var result = [], - logic = filter.logic || "and", - idx, - length, - field, - fieldInfo, - type, - format, - operator, - value, - ablType, - //filters = (filter.filters) ? filter.filters : [filter], - filters = filter.filters || [filter], - - whereOperators = { - eq: "=", - neq: "<>", - gt: ">", - gte: ">=", - lt: "<", - lte: "<=", - contains : "INDEX", - doesnotcontain: "INDEX", - endswith: "R-INDEX", - startswith: "BEGINS", - isnull: "ISNULL", - isnotnull: "ISNOTNULL", - isempty: "ISEMPTY", - isnotempty: "ISNOTEMPTY" - }; - - for (idx = 0, length = filters.length; idx < length; idx += 1) { - filter = filters[idx]; - field = filter.field; - value = filter.value; - - if (filter.filters) { - filter = progress.util._convertToABLWhereString(tableRef, filter); - } else { - // Use original field name instead of serialized name - if (field && tableRef._name) { - fieldInfo = tableRef._jsdo[tableRef._name]._fields[field.toLowerCase()]; - if (fieldInfo && fieldInfo.origName) { - field = fieldInfo.origName; - } - } - - operator = whereOperators[filter.operator]; - - if (operator === undefined) { - throw new Error("The operator " + filter.operator + " is not valid."); - } - - switch (filter.operator) { - case "isnull": - case "isnotnull": - case "isempty": - case "isnotempty": - value = undefined; - break; - } - - if (operator && value !== undefined) { - type = progress.util._getObjectType(value); - - // We need to build a template format string for the where string. - // We'll first add positional info for the value - if (type === STRING_OBJECT_TYPE) { - format = "'{1}'"; - value = value.replace(/'/g, "~'"); - } else if (type === DATE_OBJECT_TYPE) { - ablType = tableRef._getABLType(field); - if (ablType === "DATE") { - format = "DATE({1:MM, dd, yyyy})"; - } else if (ablType === "DATETIME-TZ") { - // zzz here means to translate timezone offset into minutes - format = "DATETIME-TZ({1:MM, dd, yyyy, hh, mm, ss, fff, zzz})"; - } else { - format = "DATETIME({1:MM, dd, yyyy, hh, mm, ss, fff})"; - } - } else { - format = "{1}"; - } - - // Most where strings are in the format: field operator value. Ex. custnum < 100 - // An exception to this is INDEX() and R-INDEX() which have format: operator field value - // Ex. R-INDEX(name, "LTD") - if (operator === "INDEX" || operator === "R-INDEX") { - if (type !== STRING_OBJECT_TYPE) { - throw new Error("Error parsing filter object. The operator " + filter.operator + - " requires a string value"); - } - if (filter.operator === "doesnotcontain") { - format = "{0}(" + "{2}, " + format + ") = 0"; - } else if (filter.operator === "contains") { - format = "{0}(" + "{2}, " + format + ") > 0"; - } else { // else filter.operator = "endswith" - format = "{2} MATCHES '*{1}'"; - } - } else { - format = "{2} {0} " + format; - } - - filter = progress.util._format(format, operator, value, field); - } else if (operator && value === undefined) { - if (filter.operator === "isempty" || filter.operator === "isnotempty") { - ablType = tableRef._getABLType(field); - if (ablType !== CHARACTER_ABL_TYPE) { - throw new Error("Error parsing filter object. The operator " + filter.operator + - " requires a CHARACTER field"); - } - if (filter.operator === "isempty") { - format = "{2} = ''"; - } else if (filter.operator === "isnotempty") { - format = "{2} <> ''"; - } - } else { - if (filter.operator === "isnull") { - format = "{2} = ?"; - } else if (filter.operator === "isnotnull") { - format = "{2} <> ?"; - } else { - format = "{2} {0} ?"; - } - } - - // format, operator {0}, value {1}, field {2} - filter = progress.util._format(format, operator, value, field); - } - } - - result.push(filter); - } - - filter = result.join(" " + logic + " "); - - if (result.length > 1) { - filter = "(" + filter + ")"; - } - - return filter; - }; - - - /* - * Converts the specified filter object to an SQL Query String. - * - * @param tableName - tableName of table in jsdo, where clause is applied to. - * @param filter - the filter object to convert. - * - * @returns - translated SQL where clause. - */ - progress.util._convertToSQLQueryString = function (tableRef, filter, addSelect) { - var result = [], - logic = filter.logic || "and", - idx, - length, - field, - type, - format, - operator, - value, - fieldFormat, - filters = filter.filters || [filter], - filterStr, - usingLike = true, - - whereOperators = { - eq: "=", - neq: "!=", - gt: ">", - gte: ">=", - lt: "<", - lte: "<=", - contains : "LIKE", - doesnotcontain: "NOT LIKE", - endswith: "LIKE", - startswith: "LIKE", - isnull: "ISNULL", - isnotnull: "ISNOTNULL", - isempty: "ISEMPTY", - isnotempty: "ISNOTEMPTY" - }; - - if (typeof addSelect === "undefined") { - addSelect = false; - } - - for (idx = 0, length = filters.length; idx < length; idx += 1) { - filter = filters[idx]; - field = filter.field; - value = filter.value; - - if (filter.filters) { - filterStr = progress.util._convertToSQLQueryString(tableRef, filter, false); - } else { - operator = whereOperators[filter.operator]; - - if (operator === undefined) { - throw new Error("The operator " + filter.operator + " is not valid."); - } - - switch (filter.operator) { - case "isnull": - case "isnotnull": - case "isempty": - case "isnotempty": - value = undefined; - break; - } - - if (operator && value !== undefined) { - type = progress.util._getObjectType(value); - - if (operator === "LIKE" || operator === "NOT LIKE") { - if (type !== STRING_OBJECT_TYPE) { - throw new Error("Error parsing filter object. The operator " + filter.operator + - " requires a string value"); - } - } - - if (type === STRING_OBJECT_TYPE) { - format = "'{1}'"; - value = value.replace(/'/g, "''"); - } else if (type === DATE_OBJECT_TYPE) { - fieldFormat = tableRef._getFormat(field); - if (fieldFormat === "date") { - format = "'{1:yyyy-MM-dd}'"; - } else if (fieldFormat === "date-time") { - format = "{1:#ISO(iso)}"; - } else if (fieldFormat === "time") { - format = "'{1:FFF}'"; - } - } else { - format = "{1}"; - } - - // We need to build a template format string for the where string. - // We'll first add positional info for the value, which is represented by {1} - if (filter.operator === "startswith") { - format = "'{1}%'"; - } else if (filter.operator === "endswith") { - format = "'%{1}'"; - } else if (filter.operator === "contains" || filter.operator === "doesnotcontain") { - format = "'%{1}%'"; - } else { - usingLike = false; - } - - if (usingLike) { - value = value.replace(/%/g, '\\%'); - value = value.replace(/_/g, '\\_'); - } - - format = "{2} {0} " + format; - filterStr = progress.util._format(format, operator, value, field); - } else if (operator && value === undefined) { - if (filter.operator === "isempty" || filter.operator === "isnotempty") { - type = tableRef._fields[field.toLowerCase()].type; - if (type !== STRING_OBJECT_TYPE.toLowerCase()) { - throw new Error("Error parsing filter object. The operator " + filter.operator + - " requires a string field"); - } - if (filter.operator === "isempty") { - format = "{2} = ''"; - } else if (filter.operator === "isnotempty") { - format = "{2} != ''"; - } - } else { - if (filter.operator === "isnull") { - format = "{2} IS NULL"; - } else if (filter.operator === "isnotnull") { - format = "{2} IS NOT NULL"; - } else { - format = "{2} {0} NULL"; - } - } - - // format, operator {0}, value {1}, field {2} - filterStr = progress.util._format(format, operator, value, field); - } - } - - result.push(filterStr); - } - - filterStr = result.join(" " + logic + " "); - - if (result.length > 1) { - filterStr = "(" + filterStr + ")"; - } - - if (addSelect === true) { - filterStr = "SELECT * FROM " + tableRef._name + " WHERE " + filterStr; - } - - return filterStr; - }; - - - /* - * Returns the object type; Example "String", "Date" - * Constants for object type values are defined above. - * - * @param value - the object whose type is returned - */ - progress.util._getObjectType = function (value) { - // Returns [object xxx]. Removing [object ] - return Object.prototype.toString.call(value).slice(8, -1); - }; - - - /* - * Substitutes in a variable number of arguments into specified format string (with place-holders) - * - * @param fmt - the format string with place-holders, eg. "{0} text {1}". - * - * @returns - formatted string. - */ - progress.util._format = function (fmt) { - /*jslint regexp: true*/ - var values = arguments, - formatRegExp = /\{(\d+)(:[^\}]+)?\}/g; - /*jslint regexp: false*/ - - return fmt.replace(formatRegExp, function (match, index, placeholderFormat) { - var value = values[parseInt(index, 10) + 1]; - - return progress.util._toString(value, placeholderFormat ? placeholderFormat.substring(1) : ""); - }); - - }; - - /* - * Converts the specified value param to a string. - * - * @param value - object to convert - * @param fmt - optional format string with place-holders, eg. "MM dd yyyy". - * - * @returns - converted string. - */ - progress.util._toString = function (value, fmt) { - var str; - - if (fmt) { - if (progress.util._getObjectType(value) === "Date") { - return progress.util._formatDate(value, fmt); - } - } - - if (typeof value === "number") { - str = value.toString(); - } else { - str = (value !== undefined ? value : ""); - } - - return str; - }; - - /* - * Accepts string representing number and optionally pads it with "0"'s to conform to - * specified number of digits. - * - * @param number - string representing number to pad. - * @param digit - number of digits desired for padded string. If not specified, default is 2. - * - * @returns - padded string representing number. - */ - progress.util._pad = function (number, digits) { - var zeros = ["", "0", "00", "000", "0000"], - end; - - number = String(number); - digits = digits || 2; - end = digits - number.length; - - if (end) { - return zeros[digits].substring(0, end) + number; - } - return number; - }; - - /* - * Converts the specified date param to a string. - * - * @param date - date object to convert - * @param fmt - format string with place-holders, eg. "MM dd yyyy". - * - * @returns - converted string. - */ - progress.util._formatDate = function (date, format) { - /*jslint regexp: true*/ - var dateFormatRegExp = - /dd|MM|yyyy|hh|mm|fff|FFF|ss|zzz|iso|"[^"]*"|'[^']*'/g; - /*jslint regexp: false*/ - - return format.replace(dateFormatRegExp, function (match) { - var minutes, - result, - sign; - - if (match === "dd") { - result = progress.util._pad(date.getDate()); - } else if (match === "MM") { - result = progress.util._pad(date.getMonth() + 1); - } else if (match === "yyyy") { - result = progress.util._pad(date.getFullYear(), 4); - } else if (match === "hh") { - result = progress.util._pad(date.getHours()); - } else if (match === "mm") { - result = progress.util._pad(date.getMinutes()); - } else if (match === "ss") { - result = progress.util._pad(date.getSeconds()); - } else if (match === "fff") { - result = progress.util._pad(date.getMilliseconds(), 3); - } else if (match === "FFF") { - result = String(date.getTime()); - } else if (match === "zzz") { - // timezone is returned in minutes - minutes = date.getTimezoneOffset(); - sign = minutes < 0; - result = (sign ? "+" : "-") + minutes; - } else if (match === "iso") { - result = date.toISOString(); - } - - return result !== undefined ? result : match.slice(1, match.length - 1); - }); - }; - - /* - * Processes settings in a jsdoSettings object. - * This method is used by project templates. - */ - progress.util.jsdoSettingsProcessor = function jsdoSettingsProcessor(jsdoSettings) { - if (typeof jsdoSettings === 'object') { - if (jsdoSettings.authenticationModel === undefined || jsdoSettings.authenticationModel === "") { - jsdoSettings.authenticationModel = "ANONYMOUS"; - } - } - }; - -}()); -//# sourceURL=progress.jsdo.js -/* -progress.js Version: 4.4.1-01 - -Copyright (c) 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - // "use strict"; - - var PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS = 20, - PROGRESS_JSDO_OP_STRING = ["none", "create", "read", "update", "delete", "submit"], - PROGRESS_JSDO_ROW_STATE_STRING = ["", "created", "", "modified", "deleted"]; - - /* define these if not defined yet - they may already be defined if - progress.session.js was included first */ - if (typeof progress === 'undefined') { - progress = {}; - } - if (typeof progress.data === 'undefined') { - progress.data = {}; - } - - progress.data._nextid = 0; - progress.data._uidprefix = "" + ( Date.now ? Date.now() : (new Date().getTime())); - - /* 15 - 9 */ - var UID_MAX_VALUE = 999999999999999; - - progress.data._getNextId = function () { - var uid = ++progress.data._nextid; - if (uid >= UID_MAX_VALUE) { - progress.data._nextid = uid = 1; - progress.data._uidprefix = "" + ( Date.now ? Date.now() : (new Date().getTime())); - } - - return progress.data._uidprefix + "-" + uid; - }; - - - var msg = {}; - msg.msgs = {}; -// msg numbers 0 - 99 are related to use of the API (methods and properties we expose to developers) -// 100 - 109 relate to network errors -// 110 - 998 are for miscellaneous errors - - msg.msgs.jsdoMSG000 = "JSDO, Internal Error: {1}"; - msg.msgs.jsdoMSG001 = "JSDO: JSDO has multiple tables. Please use {1} at the table reference level."; - msg.msgs.jsdoMSG002 = "JSDO: Working record for '{1}' is undefined."; - msg.msgs.jsdoMSG003 = "JSDO: {1} function requires a function as a parameter."; - msg.msgs.jsdoMSG004 = "JSDO: Unable to find resource '{1}' in the catalog."; - msg.msgs.jsdoMSG005 = "JSDO: Data for table '{1}' was not specified in addRecords() call."; - msg.msgs.jsdoMSG006 = "JSDO: Data for JSDO was not specified in addRecords() call."; - msg.msgs.jsdoMSG007 = "JSDO: Test function in {1} must return a boolean."; - msg.msgs.jsdoMSG008 = "JSDO: Invalid keyFields parameter in addRecords() call."; - msg.msgs.jsdoMSG009 = "JSDO: KeyField '{1}' in addRecords() call was not found in the schema."; - msg.msgs.jsdoMSG010 = "JSDO: Field '{1}' in relationship was not found in the schema."; - msg.msgs.jsdoMSG011 = "UIHelper: JSDO has multiple tables. " + - "Please use {1} at the table reference level."; - msg.msgs.jsdoMSG012 = "UIHelper: Invalid {2} parameter in {1} call."; - msg.msgs.jsdoMSG020 = "JSDO: tableName parameter must be a string in addRecords() call."; - msg.msgs.jsdoMSG021 = "JSDO: addMode parameter must be specified in addRecords() call."; - msg.msgs.jsdoMSG022 = "JSDO: Invalid addMode specified in addRecords() call."; - msg.msgs.jsdoMSG023 = "JSDO: Duplicate found in addRecords() call using APPEND mode."; - msg.msgs.jsdoMSG024 = "{1}: Unexpected signature in call to {2} function."; - msg.msgs.jsdoMSG025 = "{1}: Invalid parameters in call to {2} function."; - msg.msgs.jsdoMSG026 = "JSDO: saveChanges requires a " + - "CREATE, UPDATE, DELETE or SUBMIT operation to be defined."; - msg.msgs.jsdoMSG030 = "JSDO: Invalid {1}, expected {2}."; - msg.msgs.jsdoMSG031 = "JSDO: Specified sort field name '{1}' was not found in the schema."; - msg.msgs.jsdoMSG032 = "JSDO: Before-image data already exists for record in addRecords() call."; - msg.msgs.jsdoMSG033 = "{1}: Invalid signature for {2}. {3}"; - msg.msgs.jsdoMSG034 = "JSDO: In '{1}' function, JSON data is missing _id"; - msg.msgs.jsdoMSG035 = "JSDO: In '{1}' function, before-image JSON data is missing prods:clientId"; - msg.msgs.jsdoMSG036 = "JSDO: '{1}' can only be called for a dataset"; - msg.msgs.jsdoMSG037 = "{1}: Event name must be provided for {2}."; - msg.msgs.jsdoMSG038 = "Too few arguments. There must be at least {1}."; - msg.msgs.jsdoMSG039 = "The name of the event is not a string."; - msg.msgs.jsdoMSG040 = "The event listener is not a function."; - msg.msgs.jsdoMSG041 = "The event listener scope is not an object."; - msg.msgs.jsdoMSG042 = "'{1}' is not a defined event for this object."; - msg.msgs.jsdoMSG043 = "{1}: A session object was requested to check the status of a Mobile " + - "Service named '{2}', but it has not loaded the definition of that service."; - msg.msgs.jsdoMSG044 = "JSDO: In '{1}' function, {2} is missing {3} property."; - msg.msgs.jsdoMSG045 = "JSDO: {1} function: {2} is missing {3} property."; - msg.msgs.jsdoMSG046 = "JSDO: {1} operation is not defined."; - msg.msgs.jsdoMSG047 = "{1} timeout expired."; - msg.msgs.jsdoMSG048 = "{1}: {2} method has argument '{3}' that is missing property '{4}'."; - msg.msgs.jsdoMSG049 = "{1}: Unexpected error calling {2}: {3}"; - msg.msgs.jsdoMSG050 = "No token returned from server"; - msg.msgs.jsdoMSG051 = "{1} The login method was not executed because the AuthenticationProvider is already logged in."; - msg.msgs.jsdoMSG052 = "{1}: The login method was not executed because no credentials were supplied."; - msg.msgs.jsdoMSG053 = "{1}: {2} was not executed because the AuthenticationProvider is not logged in."; - msg.msgs.jsdoMSG054 = "{1}: Token refresh was not executed because the AuthenticationProvider does not have a refresh token."; - msg.msgs.jsdoMSG055 = "{1}: Token refresh was not executed because the authentication model is not sso."; - msg.msgs.jsdoMSG056 = "{1}: Already logged in."; - msg.msgs.jsdoMSG057 = "{1}: Cannot call {2} when authenticationModel is SSO. Please use the AuthenticationProvider object instead."; - msg.msgs.jsdoMSG058 = "{1}: Cannot pass username and password to addCatalog when authenticationModel " + - "is sso. Pass an AuthenticationProvider instead."; - msg.msgs.jsdoMSG059 = "{1}: Error in constructor. The authenticationModels of the " + - "AuthenticationProvider ({2}) and the JSDOSession ({3}) were not compatible."; - msg.msgs.jsdoMSG060 = "AuthenticationProvider: AuthenticationProvider is no longer logged in. " + - "Tried to refresh SSO token but failed due to authentication error at token server."; - msg.msgs.jsdoMSG061 = "{1}: Attempted to set {2} property to an invalid value."; - - // 100 - 109 relate to network errors - msg.msgs.jsdoMSG100 = "JSDO: Unexpected HTTP response. Too many records."; - msg.msgs.jsdoMSG101 = "Network error while executing HTTP request."; - - // 110 - 499 are for miscellaneous errors - msg.msgs.jsdoMSG110 = "Catalog error: idProperty not specified for resource '{1}'. " + - "idProperty is required {2}."; - msg.msgs.jsdoMSG111 = "Catalog error: Schema '{1}' was not found in catalog."; - msg.msgs.jsdoMSG112 = "Catalog error: Output parameter '{1}' was not found for operation '{2}'."; - msg.msgs.jsdoMSG113 = "Catalog error: Found xType '{1}' for output parameter '{2}' " + - "for operation '{3}' but xType DATASET, TABLE or ARRAY was expected."; - msg.msgs.jsdoMSG114 = "JSDO: idProperty '{1}' is missing from '{2}' record."; - msg.msgs.jsdoMSG115 = "JSDO: Invalid option specified in {1}() call."; - msg.msgs.jsdoMSG116 = "JSDO: {1} parameter must be a string in {2} call."; - msg.msgs.jsdoMSG117 = "JSDO: Schema from storage area '{1}' does not match JSDO schema"; - msg.msgs.jsdoMSG118 = "JSDO: Plugin '{1}' was not found."; - msg.msgs.jsdoMSG119 = "JSDO: A mappingType is expected when 'capabilities' is set." + - " Please specify a plugin (ex: JFP)."; - msg.msgs.jsdoMSG120 = "JSDO: Parameter '{2}' requires capability '{1}' in the catalog."; - msg.msgs.jsdoMSG121 = "{1}: Argument {2} must be of type {3} in {4} call."; - msg.msgs.jsdoMSG122 = "{1}: Incorrect number of arguments in {2} call. There should be {3}."; - msg.msgs.jsdoMSG123 = "{1}: A server response included an invalid '{2}' header."; - msg.msgs.jsdoMSG124 = "JSDO: autoApplyChanges is not supported for saveChanges(true) " + - "with a temp-table. Use jsdo.autoApplyChanges = false."; - msg.msgs.jsdoMSG125 = "{1}: The AuthenticationProvider is not managing valid credentials."; - msg.msgs.jsdoMSG126 = "{1}: No support for {2}."; - msg.msgs.jsdoMSG127 = "JSDO: acceptRowChanges() cannot be called for record with _rejected === true."; - - // 500 - 998 are for generic errors - msg.msgs.jsdoMSG500 = "{1}: '{2}' objects must contain a '{3}' property."; - msg.msgs.jsdoMSG501 = "{1}: '{2}' in '{3}' function cannot be an empty string."; - msg.msgs.jsdoMSG502 = "{1}: The '{2}' parameter passed to the '{3}' function has an invalid value for " + - "its '{4}' property."; - msg.msgs.jsdoMSG503 = "{1}: '{2}' must be of type '{3}'."; - msg.msgs.jsdoMSG504 = "{1}: {2} has an invalid value for the '{3}' property."; - msg.msgs.jsdoMSG505 = "{1}: '{2}' objects must have a '{3}' method."; - // use message below if invalid parameter value is an object - msg.msgs.jsdoMSG506 = "{1}: Invalid argument for the {2} parameter in {3} call."; - // use message below if invalid parameter value is a primitive - msg.msgs.jsdoMSG507 = "{1}: '{2}' is an invalid value for the {3} parameter in {4} call."; - msg.msgs.jsdoMSG508 = "JSDOSession: If a JSDOSession object is using the SSO authentication model, " + - "the options object passed to its constructor must include an authProvider property."; - msg.msgs.jsdoMSG509 = "progress.data.getSession: If the authenticationModel is AUTH_TYPE_SSO, " + - "authenticationURI and authProviderAuthenticationModel are required parameters."; - msg.msgs.jsdoMSG510 = "{1}: This session has been invalidated and cannot be used."; - msg.msgs.jsdoMSG511 = "JSDOSession: addCatalog() can only be called if an AuthenticationProvider was passed as an argument or " + - "connect() has been successfully called."; - - msg.msgs.jsdoMSG998 = "JSDO: JSON object in addRecords() must be DataSet or Temp-Table data."; - - msg.getMsgText = function (n, args) { - var text = msg.msgs[n], - i; - if (!text) { - throw new Error("Message text was not found by getMsgText()"); - } - for (i = 1; i < arguments.length; i += 1) { - text = text.replace(new RegExp('\\{' + i + '\\}', 'g'), arguments[i]); - } - - return text; - }; - - progress.data._getMsgText = msg.getMsgText; - - progress.data.PluginManager = {}; - progress.data.PluginManager._plugins = {}; - - progress.data.PluginManager.addPlugin = function(name, plugin) { - if (progress.data.PluginManager._plugins[name] === undefined) { - progress.data.PluginManager._plugins[name] = plugin; - } - else { - throw new Error("A plugin named '" + name + "' is already registered."); - } - }; - - progress.data.PluginManager.getPlugin = function (name) { - return progress.data.PluginManager._plugins[name]; - }; - - progress.data.JSIndexEntry = function JSIndexEntry(index) { - this.index = index; - }; - - progress.data.JSTableRef = function JSTableRef(jsdo, tableName) { - this._jsdo = jsdo; - this._name = tableName; - this._schema = null; - this._primaryKeys = null; - this._fields = null; - this._processed = {}; - this._visited = false; - - // record is used to represent the current record for a table reference - this.record = null; - - // Data structure - this._data = []; - this._index = {}; - this._hasEmptyBlocks = false; - - // Arrays to keep track of changes - this._beforeImage = {}; - this._added = []; - this._changed = {}; - this._deleted = []; - this._lastErrors = []; - this._convertForServer; - - this._createIndex = function () { - var i, block, id, idProperty; - this._index = {}; - this._hasEmptyBlocks = false; - for (i = 0; i < this._data.length; i += 1) { - block = this._data[i]; - if (!block) { - this._hasEmptyBlocks = true; - continue; - } - id = this._data[i]._id; - if (!id) { - idProperty = this._jsdo._resource.idProperty; - if (typeof(idProperty) == "string") { - id = this._data[i][idProperty]; - if (!id) { - throw new Error(msg.getMsgText("jsdoMSG114", idProperty, this._name)); - } - id += ""; - } - else { - id = progress.data._getNextId(); - } - this._data[i]._id = id; - } - this._index[id] = new progress.data.JSIndexEntry(i); - } - this._needCompaction = false; - }; - - this._compact = function () { - var newDataArray = [], i, block; - - for (i = 0; i < this._data.length; i += 1) { - block = this._data[i]; - if (block) { - newDataArray.push(block); - } - } - this._data = newDataArray; - this._createIndex(); - }; - - this._loadBeforeImageData = function (jsonObject, beforeImageJsonIndex, keyFields) { - var prodsBeforeData = jsonObject[this._jsdo._dataSetName]["prods:before"], - tmpIndex = {}, - record, - record2, - recordId, - key, - tmpKeyIndex, - id, - jsrecord, - tmpDataIndex, - tmpDeletedIndex, - i; - - if (prodsBeforeData && prodsBeforeData[this._name]) { - - if ((Object.keys(this._beforeImage).length !== 0) && keyFields && (keyFields.length !== 0)) { - tmpKeyIndex = {}; - for (id in this._beforeImage) { - jsrecord = this._findById(id, false); - - if (jsrecord) { - key = this._getKey(jsrecord.data, keyFields); - tmpKeyIndex[key] = jsrecord.data; - } - } - } - - for (i = 0; i < prodsBeforeData[this._name].length; i++) { - record = prodsBeforeData[this._name][i]; - tmpIndex[record["prods:id"]] = record; - - if (record["prods:rowState"] == "deleted") { - key = undefined; - - if (keyFields && (keyFields.length !== 0)) { - key = this._getKey(record, keyFields); - } - - if (tmpKeyIndex) { - if (tmpKeyIndex[key] !== undefined) { - throw new Error(msg.getMsgText("jsdoMSG032")); - } - } - - if ((tmpDataIndex === undefined) && keyFields && (keyFields.length !== 0)) { - tmpDataIndex = {}; - tmpDeletedIndex = {}; - - for (var j = 0; j < this._data.length; j++) { - record2 = this._data[j]; - if (!record2) continue; - - var key2 = this._getKey(record2, keyFields); - tmpDataIndex[key2] = record2; - } - - // We also want to check if _deleted record already exists - for (var j = 0; j < this._deleted.length; j++) { - record2 = this._deleted[j].data; - if (!record2) continue; - - var key2 = this._getKey(record2, keyFields); - tmpDeletedIndex[key2] = record2; - } - } - - // First check to see if this deleted record is already in _deleted array - if (key !== undefined) { - record2 = tmpDeletedIndex[key]; - if (record2 !== undefined) { - // If record is already in _deleted array, then nothing more to do here - continue; - } - } - - if (key !== undefined) { - record2 = tmpDataIndex[key]; - if (record2 !== undefined) { - var jsrecord = this._findById(record2._id, false); - if (jsrecord) jsrecord._remove(false); - record._id = record2._id; - } - } - - if (record._id === undefined) - record._id = progress.data._getNextId(); - var copy = {}; - this._jsdo._copyRecord( - this._tableRef, record, copy); - this._jsdo._deleteProdsProperties(copy); - this._beforeImage[record._id] = copy; - var jsrecord = new progress.data.JSRecord(this, copy); - this._deleted.push(jsrecord); - } - } - } - - // Process data using jsonObject instead of _data - // First check if there is after-data for table. Can be called with just before-image data - var tableObject = jsonObject[this._jsdo._dataSetName][this._name]; - if (tableObject) { - for (var i = 0; i < jsonObject[this._jsdo._dataSetName][this._name].length; i++) { - record = jsonObject[this._jsdo._dataSetName][this._name][i]; - recordId = undefined; - if (beforeImageJsonIndex && record["prods:id"]) { - recordId = beforeImageJsonIndex[record["prods:id"]]; - } - switch (record["prods:rowState"]) { - case "created": - if (recordId === undefined) { - recordId = record._id; - } - - // If recordId and record._id are undefined, the record was not processed - if (recordId !== undefined) { - this._beforeImage[recordId] = null; - this._added.push(recordId); - } - break; - case "modified": - var beforeRecord = tmpIndex[record["prods:id"]]; - if (beforeRecord === undefined) { - beforeRecord = {}; - } - - if (recordId === undefined) { - recordId = record._id; - } - // If recordId and record._id are undefined, the record was not processed - if (recordId !== undefined) { - beforeRecord._id = record._id; - - var copy = {}; - this._jsdo._copyRecord( - this._tableRef, beforeRecord, copy); - this._jsdo._deleteProdsProperties(copy); - - this._beforeImage[recordId] = copy; - this._changed[recordId] = record; - - this._beforeImage[beforeRecord._id] = copy; - this._changed[beforeRecord._id] = record; - } - break; - case undefined: - break; // rowState is only specified for records that have changed - default: - throw new Error(msg.getMsgText("jsdoMSG030", - "rowState value in before-image data", "'created' or 'modified'")); - } - } - } - - // Process prods:errors - var prodsErrors = jsonObject[this._jsdo._dataSetName]["prods:errors"]; - if (prodsErrors) { - for (var i = 0; i < prodsErrors[this._name].length; i++) { - var item = prodsErrors[this._name][i]; - var recordId = beforeImageJsonIndex[item["prods:id"]]; - var jsrecord = this._findById(recordId, false); - if (jsrecord) { - jsrecord.data._errorString = item["prods:error"]; - } - } - } - - tmpIndex = null; - }; - - /* - * Clears all data (including any pending changes) in buffer - */ - this._clearData = function () { - this._setRecord(null); - - // Data structure - this._data = []; - this._index = {}; - this._createIndex(); - - // Arrays to keep track of changes - this._beforeImage = {}; - this._added = []; - this._changed = {}; - this._deleted = []; - }; - - this.hasData = function () { - var data; - - // Check if we should return this table with its nested child table's data as nested - if (this._jsdo._nestChildren) { - data = this._getDataWithNestedChildren(this._data); - } - else { - data = this._getRelatedData(); - } - - if (this._hasEmptyBlocks) { - for (var i = 0; i < data.length; i++) { - var block = data[i]; - if (!block) { - return true; - } - } - } - - return data.length !== 0; - }; - - this.getData = function (params) { - var i, - data, - numEmptyBlocks, - newDataArray, - block; - - if (this._needCompaction) { - this._compact(); - } - - if (params && params.filter) { - throw new Error("Not implemented in current version"); - } - // Check if we should return this table with its nested child table's data as nested - else if (this._jsdo._nestChildren) { - data = this._getDataWithNestedChildren(this._data); - } - else { - data = this._getRelatedData(); - } - - if (this._hasEmptyBlocks) { - numEmptyBlocks = 0; - newDataArray = []; - for (i = 0; i < data.length; i += 1) { - block = data[i]; - if (block) { - newDataArray.push(block); - } - else { - numEmptyBlocks++; - } - } - if ((numEmptyBlocks * 100 / this._data.length) >= PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS) - this._needCompaction = true; - - data = newDataArray; - } - else { - // Creates a copy of the data if sort and top are specified - // so that the sorting does not happen in the JSDO memory but - // in a copy of the records - if (params && (params.sort || params.top)) { - newDataArray = []; - for (i = 0; i < data.length; i += 1) { - newDataArray.push(data[i]); - } - data = newDataArray; - } - } - - if (params && (params.sort || params.top)) { - if (params.sort) { - // Converts sort option from Kendo UI to sort option used by the JSDO - sortFields = []; - for (i = 0; i < params.sort.length; i += 1) { - field = params.sort[i].field; - if (params.sort[i].dir == "desc") { - field += ":DESC"; - } - sortFields.push(field); - } - - // Obtain sortObject from sort options to get compare functions - var sortObject = this._processSortFields(sortFields); - if (sortObject.sortFields && sortObject.sortFields.length > 0) { - sortObject.tableRef = this; - data.sort(this._getCompareFn(sortObject)); - } - } - - if (params.top) { - if (typeof(params.skip) == "undefined") { - params.skip = 0; - } - - data = data.splice(params.skip, params.top); - } - } - - return data; - }; - - this._recToDataObject = function (record, includeChildren) { - var array = [record]; - var dataObject = array; - - if (typeof(includeChildren) == 'undefined') { - includeChildren = false; - } - if (this._jsdo._dataSetName) { - dataObject = {}; - dataObject[this._jsdo._dataSetName] = {}; - dataObject[this._jsdo._dataSetName][this._name] = array; - if (includeChildren && this._children.length > 0) { - var jsrecord = this._findById(record._id, false); - if (jsrecord) { - for (var i = 0; i < this._children.length; i++) { - var tableName = this._children[i]; - dataObject[this._jsdo._dataSetName][tableName] = - this._jsdo._buffers[tableName]._getRelatedData(jsrecord); - } - } - } - } - else { - if (this._jsdo._dataProperty) { - dataObject = {}; - dataObject[this._jsdo._dataProperty] = array; - } - } - return dataObject; - }; - - this._recFromDataObject = function (dataObject) { - var data = {}; - if (dataObject) { - if (this._jsdo._dataSetName) { - if (dataObject[this._jsdo._dataSetName]) - data = dataObject[this._jsdo._dataSetName][this._name]; - } - else { - if (this._jsdo._dataProperty) { - if (dataObject[this._jsdo._dataProperty]) - data = dataObject[this._jsdo._dataProperty]; - } - else if (dataObject.data) { - data = dataObject.data; - } - else { - data = dataObject; - } - } - } - - return data instanceof Array ? data[0] : data; - }; - - // Property: schema - this.getSchema = function () { - return this._schema; - }; - this.setSchema = function (schema) { - this._schema = schema; - }; - - // Private method that returns the ABL data type for the specified field - this._getABLType = function (fieldName) { - var i, schema; - - schema = this.getSchema(); - - for (i = 0; i < schema.length; i++) { - if (schema[i].name == fieldName) { - return schema[i].ablType; - } - } - - return undefined; - }; - - // Private method that returns format property (from catalog) for the specified field - this._getFormat = function (fieldName) { - var i, schema; - - schema = this.getSchema(); - - for (i = 0; i < schema.length; i++) { - if (schema[i].name == fieldName) { - return schema[i].format; - } - } - - return undefined; - }; - - - - this.add = function (values) { - return this._add(values, true, true); - }; - - // Alias for add() method - this.create = this.add; - - this._add = function (values, trackChanges, setWorkingRecord) { - if (typeof(trackChanges) == 'undefined') { - trackChanges = true; - } - if (typeof(setWorkingRecord) == 'undefined') { - setWorkingRecord = true; - } - var record = {}, - i, - j, - value, - prefixElement, - name; - - if (typeof values === "undefined") { - values = {}; - } - - // Assign values from the schema - var schema = this.getSchema(); - for (i = 0; i < schema.length; i++) { - var fieldName = schema[i].name; - if (schema[i].type == "array") { - record[fieldName] = []; - if (schema[i].maxItems) { - for (var j = 0; j < schema[i].maxItems; j++) { - record[fieldName][j] = this._jsdo._getDefaultValue(schema[i]); - } - } - - // Assign array values from object parameter - value = values[fieldName]; - if (typeof value != "undefined") { - record[fieldName] = value; - delete values[fieldName]; - } - // Assign values from individual fields from flattened arrays - prefixElement = this._jsdo._getArrayField(fieldName); - if (!record[fieldName]) { - record[fieldName] = []; - } - for (j = 0; j < schema[i].maxItems; j += 1) { - name = prefixElement.name + (j+1); - value = values[name]; - if (typeof value != "undefined") { - if (!this._fields[name.toLowerCase()]) { - // Skip element if a field with the same name exists - // Remove property from object for element since it is not part of the actual schema - delete values[prefixElement.name + (j+1)]; - if (typeof value == 'string' && schema[i].items.type != 'string') { - value = this._jsdo._convertType(value, - schema[i].items.type, - null); - } - record[fieldName][j] = value; - } - } - } - } - else { - record[fieldName] = this._jsdo._getDefaultValue(schema[i]); - } - } - - // Assign values based on a relationship - if (this._jsdo.useRelationships && this._relationship && this._parent) { - if (this._jsdo._buffers[this._parent].record) { - for (var j = 0; j < this._relationship.length; j++) { - record[this._relationship[j].childFieldName] = - this._jsdo._buffers[this._parent].record.data[this._relationship[j].parentFieldName]; - } - } - else - throw new Error(msg.getMsgText("jsdoMSG002", this._parent)); - } - // Assign values from object parameter - for (var v in values) { - record[v] = values[v]; - } - - // Specify _id field - do not use schema default - var id; - var idProperty; - if ((idProperty = this._jsdo._resource.idProperty) !== undefined) { - id = record[idProperty]; - } - if (!id) { - id = progress.data._getNextId(); - } - else { - id += ""; - } - record._id = id; - - if (this.autoSort - && this._sortRecords - && (this._sortFn !== undefined || this._sortObject.sortFields !== undefined)) { - if (this._needsAutoSorting) { - this._data.push(record); - this._sort(); - } - else { - // Find position of new record in _data and use splice - for (var i = 0; i < this._data.length; i++) { - if (this._data[i] === null) continue; // Skip null elements - var ret = this._sortFn ? - this._sortFn(record, this._data[i]) : - this._compareFields(record, this._data[i]); - if (ret == -1) break; - } - this._data.splice(i, 0, record); - } - this._createIndex(); - } - else { - this._data.push(record); - this._index[record._id] = new progress.data.JSIndexEntry(this._data.length - 1); - } - - var jsrecord = new progress.data.JSRecord(this, record); - - // Set record property ignoring relationships - if (setWorkingRecord) - this._setRecord(jsrecord, true); - - if (trackChanges) { - // Save before image - this._beforeImage[record._id] = null; - // End - Save before image - this._added.push(record._id); - } - return jsrecord; - }; - - /* - * Returns records related to the specified jsrecord. - * If jsrecord is not specified the parent working record is used. - */ - this._getRelatedData = function (jsrecord) { - var data = []; - - if (this._data.length === 0) return data; - - if (typeof(jsrecord) == 'undefined') { - if (this._jsdo.useRelationships && this._relationship && this._parent) { - jsrecord = this._jsdo._buffers[this._parent].record; - if (!jsrecord) - throw new Error(msg.getMsgText("jsdoMSG002", this._parent)); - } - } - if (jsrecord) { - // Filter records using relationship - for (var i = 0; i < this._data.length; i++) { - var block = this._data[i]; - if (!block) continue; - - var match = false; - for (var j = 0; j < this._relationship.length; j++) { - match = (jsrecord.data[this._relationship[j].parentFieldName] == - this._data[i][this._relationship[j].childFieldName]); - if (!match) break; - } - if (match) - data.push(this._data[i]); - } - } - else - data = this._data; - - return data; - }; - - - // This method is called on a parent table that has child tables - // where the relationship is specified as NESTED. - // It returns a json array that contains the parent rows. - // If a parent row is involved in nested relationship, - // then references to the child rows are added - // to the parent row in a child table array (providing the nested format) - // We are using the internal jsdo _data arrays, - // and adding a child table array to each parent row that has children. - // Once the caller is done with the nested data, they can call jsdo._unnestData() - // which removes these child table references - this._getDataWithNestedChildren = function (data) { - - // Walk through all the rows and determine if any of its child tables - // should be associated (nested) with the current record - for (var i = 0; i < data.length; i++) { - var parentRecord = data[i]; - - // Now walk thru the parent's children to find any nested children - if (this._children && this._children.length > 0) { - for (var j = 0; j < this._children.length; j++) { - var childBuf = this._jsdo._buffers[this._children[j]]; - - if (childBuf._isNested) { - // If child is nested, then we should walk child records to find matches - for (var k = 0; k < childBuf._data.length; k++) { - var childRecord = childBuf._data[k]; - if (!childRecord) continue; - - var match = false; - for (var m = 0; m < childBuf._relationship.length; m++) { - match = (parentRecord[childBuf._relationship[m].parentFieldName] == - childRecord[childBuf._relationship[m].childFieldName]); - if (!match) break; - } - if (match) { - // Make sure that this parentRecord has an array for its child rows - if (!parentRecord[childBuf._name]) { - parentRecord[childBuf._name] = []; - } - parentRecord[childBuf._name].push(childRecord); - } - - - } // end for; finished adding all child rows for parentRecord - - // The child table may have its own nested children so call recursively - // Use child row array in current parentRecord - if (childBuf._hasNestedChild()) { - childBuf._getDataWithNestedChildren(parentRecord[childBuf._name]); - } - - - } // end if (childBuf._isNested) - } - } - - - } - return data; - - }; - - this._findFirst = function () { - if (this._jsdo.useRelationships && this._relationship && this._parent) { - if (this._jsdo._buffers[this._parent].record) { - // Filter records using relationship - for (var i = 0; i < this._data.length; i++) { - var block = this._data[i]; - if (!block) continue; - - var match = false; - var parentFieldName, childFieldName; - for (var j = 0; j < this._relationship.length; j++) { - parentFieldName = this._relationship[j].parentFieldName; - childFieldName = this._relationship[j].childFieldName; - match = (this._jsdo._buffers[this._parent].record.data[parentFieldName] == - this._data[i][childFieldName]); - if (!match) break; - } - if (match) { - return new progress.data.JSRecord(this, this._data[i]); - } - } - } - } - else { - for (var i = 0; i < this._data.length; i++) { - var block = this._data[i]; - if (!block) continue; - - return new progress.data.JSRecord(this, this._data[i]); - } - } - - - return undefined; - }; - - this._setRecord = function (jsrecord, ignoreRelationships) { - if (jsrecord) { - this.record = jsrecord; - } - else { - this.record = undefined; - } - - // Set child records only if useRelationships is true - if (this._jsdo.useRelationships) { - ignoreRelationships = ((typeof(ignoreRelationships) == 'boolean') && ignoreRelationships); - - if (this._children && this._children.length > 0) { - for (var i = 0; i < this._children.length; i++) { - var childTable = this._jsdo._buffers[this._children[i]]; - if (!ignoreRelationships && this.record && childTable._relationship) { - childTable._setRecord(childTable._findFirst()); - } - else { - childTable._setRecord(undefined, ignoreRelationships); - } - } - } - } - - if (this._jsdo._defaultTableRef) { - this._jsdo.record = this.record; - } - }; - - this.assign = function (values) { - if (this.record) { - return this.record.assign(values); - } - else - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - }; - - // Alias for assign() method - this.update = this.assign; - - this.remove = function () { - if (this.record) { - return this.record._remove(true); - } - else - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - }; - - this._remove = function (bTrackChanges) { - if (this.record) { - return this.record._remove(bTrackChanges); - } - else - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - }; - - this.getId = function () { - if (this.record) { - return this.record.data._id; - } - else - return 0; - }; - - // getErrors() - JSTableRef - this.getErrors = function () { - return this._lastErrors; - }; - - this.getErrorString = function () { - if (this.record) { - return this.record.data._errorString; - } - else - return 0; - }; - - this.findById = function (id) { - return this._findById(id, true); - }; - - this._findById = function (id, setWorkingRecord) { - if (typeof(setWorkingRecord) == 'undefined') { - setWorkingRecord = true; - } - if (id && this._index[id]) { - var record = this._data[this._index[id].index]; - this.record = record ? (new progress.data.JSRecord(this, record)) : null; - if (setWorkingRecord) - this._setRecord(this.record); - return this.record; - } - - if (setWorkingRecord) - this._setRecord(null); - return null; - }; - - /* - * Finds a record in the JSDO memory using the specified function to determine the record. - */ - this.find = function (fn) { - if (typeof(fn) != 'function') { - throw new Error(msg.getMsgText("jsdoMSG003", "find()")); - } - var data = this._getRelatedData(); - - for (var i = 0; i < data.length; i++) { - var block = data[i]; - if (!block) { - continue; - } - this._setRecord(new progress.data.JSRecord(this, data[i])); - var result = fn(this.record); - if (typeof(result) != 'boolean') { - throw new Error(msg.getMsgText("jsdoMSG007", "find()")); - } - if (result) { - return this.record; - } - } - - this._setRecord(null); - return null; - }; - - /* - * Loops through the records - */ - this.foreach = function (fn) { - if (typeof(fn) != 'function') { - throw new Error(msg.getMsgText("jsdoMSG003", "foreach()")); - } - var numEmptyBlocks = 0; - if (this._needCompaction) - this._compact(); - - var data = this._getRelatedData(); - - this._inforeach = true; - for (var i = 0; i < data.length; i++) { - var block = data[i]; - if (!block) { - numEmptyBlocks++; - continue; - } - - this._setRecord(new progress.data.JSRecord(this, data[i])); - var result = fn(this.record); - if ((typeof(result) != 'undefined') && !result) - break; - } - - this._inforeach = false; - - if ((numEmptyBlocks * 100 / this._data.length) >= PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS) - this._needCompaction = true; - }; - - this._equalRecord = function (rec1, rec2, keyFields) { - var field; - var match = true; - for (var i = 0; i < keyFields.length; i++) { - var fieldName = keyFields[i]; - var value1 = rec1[fieldName]; - var value2 = rec2[fieldName]; - - if (!jsdo[tableName].caseSensitive) { - field = jsdo[tableName]._fields[fieldName.toLowerCase()]; - if (field && field.type == "string") { - if (value1 !== undefined && value1 !== null) - value1 = value1.toUpperCase(); - if (value2 !== undefined && value2 !== null) - value2 = value2.toUpperCase(); - } - } - - match = (value1 == value2); - if (!match) return false; - } - return true; - }; - - // Private method to merge changes using merge modes: APPEND, EMPTY, MERGE and REPLACE - this._getKey = function (record, keyFields) { - var keyObject = {}; - for (var i = 0; i < keyFields.length; i++) { - var fieldName = keyFields[i]; - var value = record[fieldName]; - - if (!jsdo[tableName].caseSensitive) { - field = jsdo[tableName]._fields[fieldName.toLowerCase()]; - if (field && field.type == "string") { - if (value !== undefined && value !== null) - value = value.toUpperCase(); - } - } - keyObject[fieldName] = value; - } - return JSON.stringify(keyObject); - }; - - this._getCompareFn = function (sortObject) { - if (typeof sortObject == 'function') { - return function (rec1, rec2) { - if (rec1 === null) return 1; - if (rec2 === null) return -1; - - var jsrec1 = new progress.data.JSRecord(this, rec1); - var jsrec2 = new progress.data.JSRecord(this, rec2); - return sortObject(jsrec1, jsrec2); - }; - } - else return function (rec1, rec2) { - var tableRef = sortObject.tableRef; - var sortFields = sortObject.sortFields; - if (!(sortFields instanceof Array)) return 0; - var sortAscending = sortObject.sortAscending; - - if (rec1 === null) return 1; - if (rec2 === null) return -1; - - var field; - for (var i = 0; i < sortFields.length; i++) { - var fieldName = sortFields[i]; - var value1 = rec1[fieldName]; - var value2 = rec2[fieldName]; - - if (!tableRef.caseSensitive) { - field = tableRef._fields[fieldName.toLowerCase()]; - if (field && field.type == "string") { - if (value1 !== undefined && value1 !== null) - value1 = value1.toUpperCase(); - if (value2 !== undefined && value2 !== null) - value2 = value2.toUpperCase(); - } - } - if (value1 > value2 || (value1 === undefined || value1 === null)) - return sortAscending[i] ? 1 : -1; - else if (value1 < value2 || (value2 === undefined && value2 === null)) - return sortAscending[i] ? -1 : 1; - } - return 0; - }; - }; - - this._sortObject = {}; - this._sortObject.tableRef = this; - this._sortObject.sortFields = undefined; - this._sortObject.sortAscending = undefined; - this._compareFields = this._getCompareFn(this._sortObject); - - // _sortRecords - Tells the table reference whether to sort on add, assign and addRecords - this._sortRecords = true; - // Tells the table reference whether an autoSort is required on an add or assign - this._needsAutoSorting = false; - this._sortFn = undefined; - if ((typeof Object.defineProperty) == 'function') { - this._autoSort = true; - Object.defineProperty( - this, - "autoSort", - { - get: function () { - return this._autoSort; - }, - set: function (value) { - if (value) { - this._autoSort = true; - if (this._sortFn || this._sortObject.sortFields) { - this._sort(); - this._createIndex(); - } - } - else - this._autoSort = false; - }, - enumerable: true, - writeable: true - }); - this._caseSensitive = false; - Object.defineProperty( - this, - "caseSensitive", - { - get: function () { - return this._caseSensitive; - }, - set: function (value) { - if (value) { - this._caseSensitive = true; - } - else - this._caseSensitive = false; - if (this.autoSort && - (this._sortObject.sortFields && !this._sortFn)) { - this._sort(); - this._createIndex(); - } - }, - enumerable: true, - writeable: true - }); - } - else { - this.autoSort = true; - this.caseSensitive = false; // caseSensitive is false by default - } - - this._processSortFields = function (sortFields) { - var sortObject = {}; - if (sortFields instanceof Array) { - sortObject.sortFields = sortFields; - sortObject.sortAscending = []; - sortObject.fields = {}; - for (var i = 0; i < sortObject.sortFields.length; i++) { - var idx; - var fieldName; - var field; - - if (typeof (sortObject.sortFields[i]) != 'string') { - throw new Error(msg.getMsgText("jsdoMSG030", "sort field name", "string element")); - } - if ((idx = sortObject.sortFields[i].indexOf(':')) != -1) { - fieldName = sortObject.sortFields[i].substring(0, idx); - var sortOrder = sortObject.sortFields[i].substring(idx + 1); - switch (sortOrder.toUpperCase()) { - case 'ASCENDING': - case 'ASC': - sortObject.sortAscending[i] = true; - break; - case 'DESCENDING': - case 'DESC': - sortObject.sortAscending[i] = false; - break; - default: - throw new Error(msg.getMsgText("jsdoMSG030", - "sort order '" + sortObject.sortFields[i].substring(idx + 1) + "'", - "ASCENDING or DESCENDING")); - } - } - else { - fieldName = sortObject.sortFields[i]; - sortObject.sortAscending[i] = true; - } - if (fieldName != "_id" && this._fields) { - field = this._fields[fieldName.toLowerCase()]; - if (field) { - if (field.type == "array") - throw new Error(msg.getMsgText("jsdoMSG030", "data type found in sort", - "scalar field")); - fieldName = field.name; - } - else - throw new Error(msg.getMsgText("jsdoMSG031", fieldName)); - } - sortObject.sortFields[i] = fieldName; - sortObject.fields[fieldName] = fieldName; - } - } - else { - sortObject.sortFields = undefined; - sortObject.sortAscending = undefined; - sortObject.fields = undefined; - } - return sortObject; - }; - - this.setSortFields = function (sortFields) { - if (sortFields === undefined || sortFields === null) { - this._sortObject.sortFields = undefined; - this._sortObject.sortAscending = undefined; - } - else if (sortFields instanceof Array) { - var sortObject = this._processSortFields(sortFields); - this._sortObject.sortFields = sortObject.sortFields; - this._sortObject.sortAscending = sortObject.sortAscending; - this._sortObject.fields = sortObject.fields; - - if (this.autoSort) { - this._sort(); - this._createIndex(); - } - } - else - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "setSortFields()")); - }; - - this.setSortFn = function (fn) { - // Check that fn parameter is a function - // Valid values are a function, undefined, or null - // Documentation mentions null as a way to clear the sort function - if (fn && typeof (fn) != 'function') { - throw new Error(msg.getMsgText("jsdoMSG030", "parameter in setSortFn()", - "function parameter")); - } - this._sortFn = fn ? this._getCompareFn(fn) : undefined; - if (this.autoSort) { - this._sort(); - this._createIndex(); - } - }; - - this.sort = function (arg1) { - if (arg1 === undefined || arg1 === null) { - throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "sort()")); - } - if (arguments.length !== 1 || - (!(arg1 instanceof Array) && typeof(arg1) != 'function')) { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "sort()")); - } - - if (arg1 instanceof Array) { - var sortObject = this._processSortFields(arg1); - if (sortObject.sortFields && sortObject.sortFields.length > 0) - this._sort(sortObject); - } - else { - this._sort(arg1); - } - this._createIndex(); - }; - - this._sort = function (arg1) { - if (arguments.length === 0 && - (!this.autoSort || (this._sortFn === undefined && this._sortObject.sortFields === undefined))) - return; - - if (arguments.length === 0) { - if (this._sortFn) { - // Sort using function - this._data.sort(this._sortFn); - } - else { - // Sort using sort fields - this._data.sort(this._compareFields); - } - this._needsAutoSorting = false; - } - else { - if (typeof(arg1) == 'function') { - // Sort using function - this._data.sort(this._getCompareFn(arg1)); - } - else { - // Sort using sort fields - arg1.tableRef = this; - this._data.sort(this._getCompareFn(arg1)); - } - if (this.autoSort) - this._needsAutoSorting = true; - } - }; - - /* - * Reads a JSON object into the JSDO memory for the specified table reference. - */ - this.addRecords = function (jsonObject, addMode, keyFields, trackChanges, isInvoke) { - this._jsdo._addRecords(this._name, jsonObject, addMode, keyFields, trackChanges, isInvoke); - }; - - /* - * Accepts changes for the specified table reference. - */ - this.acceptChanges = function () { - var tableRef = this; - - // First, let's remove any "prods:" properties from created and updated records. - // Don't have to worry about deleted records, since they're going away. - for (var id in tableRef._beforeImage) { - // Create - if (tableRef._beforeImage[id] === null) { - var jsrecord = tableRef._findById(id, false); - if (jsrecord !== null) { - tableRef._jsdo._deleteProdsProperties(jsrecord.data, true); - } - - } - // Update - else if (this._changed[id] !== undefined) { - var jsrecord = this._findById(id, false); - if (jsrecord !== null) { - tableRef._jsdo._deleteProdsProperties(jsrecord.data, true); - } - } - } - - tableRef._processed = {}; - tableRef._added = []; - tableRef._changed = {}; - tableRef._deleted = []; - tableRef._beforeImage = {}; - }; - - /* - * Rejects changes for the specified table reference. - */ - this.rejectChanges = function () { - // Reject changes - for (var id in this._beforeImage) { - if (this._beforeImage[id] === null) { - // Undo create - this._jsdo._undoCreate(this, id); - } - else if (this._changed[id] !== undefined) { - // Undo update - this._jsdo._undoUpdate(this, id, true); - } - else { - // Undo delete - this._jsdo._undoDelete(this, id, true); - } - } - - var tableRef = this; - tableRef._processed = {}; - tableRef._added = []; - tableRef._changed = {}; - tableRef._deleted = []; - }; - - this.hasChanges = function () { - return (Object.keys(this._beforeImage).length !== 0); - }; - - this.getChanges = function () { - var result = []; - for (var id in this._beforeImage) { - var item = {rowState: "", record: null}; - // Create - if (this._beforeImage[id] === null) { - item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_CREATE]; - item.record = this._findById(id, false); - } - // Update - else if (this._changed[id] !== undefined) { - item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_UPDATE]; - item.record = this._findById(id, false); - } - // Delete - else { - item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_DELETE]; - item.record = new progress.data.JSRecord(this, this._beforeImage[id]); - } - result.push(item); - } - return result; - }; - - /* - * Private method to apply changes for the specified table reference. - * If _errorString has been set for a row, row change is rejected. - * If it has not been set, acceptRowChanges() is called. - */ - this._applyChanges = function () { - for (var id in this._beforeImage) { - // Create - if (this._beforeImage[id] === null) { - var jsrecord = this._findById(id, false); - if (jsrecord !== null) { - if (jsrecord.data._rejected - || (jsrecord.data._errorString !== undefined)) { - this._jsdo._undoCreate(this, id); - } - else { - jsrecord.acceptRowChanges(); - } - } - else { - // Record not present in JSDO memory - // Delete after Create - var found = false; - for (var i = 0; i < this._deleted.length; i++) { - found = (this._deleted[i].data._id == id); - if (found) break; - } - if (!found) { - throw new Error(msg.getMsgText("jsdoMSG000", - "Created record appears to be deleted without a delete operation.")); - } - } - } - // Update - else if (this._changed[id] !== undefined) { - var jsrecord = this._findById(id, false); - if (jsrecord !== null) { - // Record found in JSDO memory - if (jsrecord.data._rejected - || (jsrecord.data._errorString !== undefined)) { - this._jsdo._undoUpdate(this, id); - } - else { - jsrecord.acceptRowChanges(); - } - } - else { - // Record not present in JSDO memory - // Delete after Update - if (this._beforeImage[id]._rejected - || (this._beforeImage[id]._errorString !== undefined)) { - this._jsdo._undoDelete(this, id); - } - else { - var found = false; - for (var i = 0; i < this._deleted.length; i++) { - found = (this._deleted[i].data._id == id); - if (found) break; - } - if (!found) { - throw new Error(msg.getMsgText("jsdoMSG000", - "Updated record appears to be deleted without a delete operation.")); - } - } - } - } - // Delete - else { - if (this._beforeImage[id]._rejected - || (this._beforeImage[id]._errorString !== undefined)) { - this._jsdo._undoDelete(this, id); - } - } - } - - var tableRef = this; - tableRef._processed = {}; - tableRef._added = []; - tableRef._changed = {}; - tableRef._deleted = []; - tableRef._beforeImage = {}; - }; - - - /* - * Accepts row changes for the working record at the table reference level. - */ - this.acceptRowChanges = function () { - if (this.record) - return this.record.acceptRowChanges(); - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - }; - - /* - * Rejects row changes for the working record at the table reference level. - */ - this.rejectRowChanges = function () { - if (this.record) - return this.record.rejectRowChanges(); - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - }; - - - /* This method returns true - * if this table has any child tables and at least one of those tables is nested. - * Else if returns false. - */ - this._hasNestedChild = function () { - var hasNestedChild = false; - var childBufObj; - - // If table has children, see if any relationship is NESTED - if (this._children.length > 0) { - for (var i = 0; i < this._children.length; i++) { - childBufObj = this._jsdo._buffers[this._children[i]]; - - if (childBufObj._isNested) { - hasNestedChild = true; - break; - } - } - } - - return hasNestedChild; - }; - }; - - /* - * Returns a JSRecord for the specified JSDO. - * @param jsdo the JSDO - * @param record the values of the record - */ - progress.data.JSRecord = function JSRecord(tableRef, record) { - this._tableRef = tableRef; - this.data = record; - - this.getId = function () { - return this.data._id ? this.data._id : null; - }; - - this.getErrorString = function () { - return this.data._errorString; - }; - - /* - * Saves a copy of the current record to the before image. - */ - this._saveBeforeImageUpdate = function () { - // Save before image - if (this._tableRef._beforeImage[this.data._id] === undefined) { - // this.data._index = index; - var copy = {}; - this._tableRef._jsdo._copyRecord( - this._tableRef, this.data, copy); - this._tableRef._beforeImage[this.data._id] = copy; - } - - if (this._tableRef._changed[this.data._id] === undefined) { - this._tableRef._changed[this.data._id] = this.data; - } - // End - Save before image - }; - - /* - * - */ - this._sortRecord = function (fields) { - var index = this._tableRef._index[this.data._id].index; - var record = this._tableRef._data[index]; - - if (this._tableRef.autoSort - && this._tableRef._sortRecords - && (this._tableRef._sortFn !== undefined - || this._tableRef._sortObject.sortFields !== undefined)) { - - if (this._tableRef._sortObject.fields) { - if (typeof fields == 'string') { - if (this._tableRef._sortObject.fields[fields] === undefined) - return; // Only sort records if the the specified field is in the sort fields - } - else if (fields instanceof Array) { - var found = false; - for (var i = 0; i < fields.length; i++) { - if (this._tableRef._sortObject.fields[fields[i]] !== undefined) { - found = true; - break; - } - } - if (!found) - return; // Only sort records if the the specified fields are in the sort fields - } - } - - if (this._tableRef._needsAutoSorting) { - this._tableRef._sort(); - this._tableRef._createIndex(); - } - else { - // Find position of new record in _data and use splice - for (var i = 0; i < this._tableRef._data.length; i++) { - if (this._tableRef._data[i] === null) continue; // Skip null elements - if (i == index) continue; // Skip changed record - var ret = this._tableRef._sortFn ? - this._tableRef._sortFn(record, this._tableRef._data[i]) : - this._tableRef._compareFields(record, this._tableRef._data[i]); - if (ret == -1) break; - } - - if (i > index) { - i--; - } - if (i != index) { - this._tableRef._data.splice(index, 1); - this._tableRef._data.splice(i, 0, record); - this._tableRef._createIndex(); - } - } - } - }; - - /* - * Assigns the specified values. - * @param record parameter with the record values - */ - this.assign = function (record) { - if (record === undefined) - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "assign() or update()")); - - this._saveBeforeImageUpdate(); - - var fieldName, - i, - j, - value, - schema = this._tableRef.getSchema(), - prefixElement, - name; - - if (record) { - for (i = 0; i < schema.length; i += 1) { - fieldName = schema[i].name; - value = record[fieldName]; - if (typeof value != "undefined") { - if (typeof value == 'string' && schema[i].type != 'string') { - value = this._tableRef._jsdo._convertType(value, - schema[i].type, - schema[i].items ? schema[i].items.type : null); - } - this.data[fieldName] = value; - } - if (schema[i].type === "array") { - // Assign values from individual fields from flattened arrays - prefixElement = this._tableRef._jsdo._getArrayField(fieldName); - if (!this.data[fieldName]) { - this.data[fieldName] = []; - } - for (j = 0; j < schema[i].maxItems; j += 1) { - name = prefixElement.name + (j+1); - value = record[name]; - if (typeof value != "undefined") { - // Skip element if a field with the same name exists - if (!this._tableRef._fields[name.toLowerCase()]) { - if (typeof value == 'string' && schema[i].items.type != 'string') { - value = this._tableRef._jsdo._convertType(value, - schema[i].items.type, - null); - } - this.data[fieldName][j] = value; - } - } - } - } - } - - this._sortRecord(); - } - return true; - }; - - // Alias for assign() method - this.update = this.assign; - - /* - * Removes the JSRecord. - */ - this.remove = function () { - return this._remove(true); - }; - - this._remove = function (bTrackChanges) { - if (typeof(bTrackChanges) == 'undefined') { - bTrackChanges = true; - } - - var index = this._tableRef._index[this.data._id].index; - var jsrecord = this._tableRef._findById(this.data._id, false); - - if (bTrackChanges) { - // Save before image - var record = this._tableRef._beforeImage[this.data._id]; - if (record === undefined) { - // Record does not exist in the before image - this.data._index = index; - this._tableRef._beforeImage[this.data._id] = this.data; - } - else { - // Record exists in the before image - if (record) { - // Record is not null - a null entry in the before image indicates - // corresponds to an add - // Save the index of the record - // so that an undo would restore the record in the same position in _data - record._index = index; - } - } - // End - Save before image - this._tableRef._deleted.push(jsrecord); - } - - // Set entry to null instead of removing entry - index requires positions to be persistent - this._tableRef._data[index] = null; - this._tableRef._hasEmptyBlocks = true; - delete this._tableRef._index[this.data._id]; - - // Set record property - this._tableRef._setRecord(null); - - return true; - }; - - /* - * Accepts row changes for the specified record. - */ - this.acceptRowChanges = function () { - var id = this.data._id; - if (this._tableRef._beforeImage[id] !== undefined) { - if (this.data._rejected) { - throw new Error(msg.getMsgText("jsdoMSG127")); - } - if (this._tableRef._beforeImage[id] === null) { - // Accept create - // Remove element from _added - for (var i = 0; i < this._tableRef._added.length; i++) { - if (this._tableRef._added[i] == id) { - this._tableRef._added.splice(i, 1); - break; - } - } - this._tableRef._jsdo._deleteProdsProperties(this.data, true); - } - else if (this._tableRef._changed[id] !== undefined) { - // Accept update - delete this._tableRef._changed[id]; - this._tableRef._jsdo._deleteProdsProperties(this.data, true); - } - else { - // Accept delete - // Remove element from _deleted - for (var i = 0; i < this._tableRef._deleted.length; i++) { - if (this._tableRef._deleted[i].data._id == id) { - this._tableRef._deleted.splice(i, 1); - break; - } - } - } - delete tableRef._beforeImage[id]; - } - }; - - /* - * Rejects row changes for the specified record. - */ - this.rejectRowChanges = function () { - var id = this.data._id; - if (this._tableRef._beforeImage[id] !== undefined) { - if (this._tableRef._beforeImage[id] === null) { - // Undo create - this._tableRef._jsdo._undoCreate(this._tableRef, id); - // Remove element from _added - for (var i = 0; i < this._tableRef._added.length; i++) { - if (this._tableRef._added[i] == id) { - this._tableRef._added.splice(i, 1); - break; - } - } - } - else if (this._tableRef._changed[id] !== undefined) { - // Undo update - this._tableRef._jsdo._undoUpdate(this._tableRef, id, true); - delete this._tableRef._changed[id]; - } - else { - // Undo delete - this._tableRef._jsdo._undoDelete(this._tableRef, id, true); - // Remove element from _deleted - for (var i = 0; i < this._tableRef._deleted.length; i++) { - if (this._tableRef._deleted[i].data._id == id) { - this._tableRef._deleted.splice(i, 1); - break; - } - } - } - delete tableRef._beforeImage[id]; - } - }; - - }; - - /* - * Returns a JSDO for the specified resource. - * @param resNameOrParmObj: the resource name or an object that contains the initial values for the JSDO - * (if this is an object, it should include the name property with the resource name - * @param serviceName : name of service (ignored if 1st param is an object containing the initial values) - */ - progress.data.JSDO = function JSDO(resNameOrParmObj, serviceName) { - var _super = {}; - - if (typeof progress.data.Session == 'undefined') { - throw new Error('ERROR: You must include progress.session.js'); - } - - _super.subscribe = this.subscribe; - - // Override for Observable.subscribe - this.subscribe = function(evt) { - var args = Array.prototype.slice.call(arguments); - if (typeof evt === "string") { - // Aliases for events - switch(evt.toLowerCase()) { - case "beforeread": - args[0] = "beforefill"; - break; - case "afterread": - args[0] = "afterfill"; - break; - } - } - _super.subscribe.apply(this, args); - }; - - this._defineProperty = function (tableName, fieldName) { - Object.defineProperty( - this._buffers[tableName], - fieldName, - { - get: function fnGet() { - var name, - index, - element, - fieldInfo; - if (this.record) { - index = fieldName.indexOf(progress.data.JSDO.ARRAY_INDEX_SEPARATOR); - if (index > 0 && !this._fields[fieldName.toLowerCase()]) { - // Skip element if a field with the same name exists - // Check if field is a flattened array field by quickly checking for the separator - // Extract name and index element - name = fieldName.substring(0, index); - element = fieldName.substring(index + progress.data.JSDO.ARRAY_INDEX_SEPARATOR.length); - fieldInfo = this._fields[name.toLowerCase()]; - if (!isNaN(element) && fieldInfo && (fieldInfo.type === "array")) { - return this.record.data[name][element - 1]; - } - } - return this.record.data[fieldName]; - } - else - return null; - }, - set: function (value) { - var name = fieldName, - index, - element, - fieldInfo; - if (this.record) { - this.record._saveBeforeImageUpdate(); - - try { - index = fieldName.indexOf(progress.data.JSDO.ARRAY_INDEX_SEPARATOR); - if (index > 0 && !this._fields[fieldName.toLowerCase()]) { - // Skip element if a field with the same name exists - name = fieldName.substring(0, index); - element = fieldName.substring(index + progress.data.JSDO.ARRAY_INDEX_SEPARATOR.length); - fieldInfo = this._fields[name.toLowerCase()]; - if (!isNaN(element) && fieldInfo && (fieldInfo.type === "array")) { - this.record.data[name][element - 1] = value; - return; - } - } - this.record.data[fieldName] = value; - } - finally { - this.record._sortRecord(name); - } - } - }, - enumerable: true, - writeable: true - }); - }; - - // Initial values - this._buffers = {}; // Object of table references - this._numBuffers = 0; - this._defaultTableRef = null; - - this._async = true; - this._dataProperty = null; - this._dataSetName = null; - this.operations = []; - this.useRelationships = true; - - this._session = null; - this._needCompaction = false; - - this._hasCUDOperations = false; - this._hasSubmitOperation = false; - this._useSubmit = false; // For saving saveChanges(useSubmit) param - - this.autoApplyChanges = true; // default should be true to support 11.2 behavior - this._lastErrors = []; - this._localStorage = null; - this._convertForServer; - var autoFill = false; - - // Initialize JSDO using init values - if (!arguments[0]) { - throw new Error("JSDO: Parameters are required in constructor."); - } - - if (typeof(arguments[0]) == "string") { - this.name = arguments[0]; -// if ( arguments[1] && (typeof(arguments[1]) == "string") ) -// localServiceName = serviceName; - } - else if (typeof(arguments[0]) == "object") { - var args = arguments[0]; - for (var v in args) { - switch (v) { - case 'autoFill': - autoFill = args[v]; - break; - case 'events': - this._events = {}; - for (var eventName in args[v]) { - this._events[eventName.toLowerCase()] = args[v][eventName]; - } - break; - case 'dataProperty': - this._dataProperty = args[v]; - break; - default: - this[v] = args[v]; - } - } - } - /* error out if caller didn't pass the resource name */ - if ((!this.name) /*|| !(this._session)*/) { - // make this error message more specific? - throw new Error("JSDO: JSDO constructor is missing the value for 'name'"); - } - - /* perform some basic validation on the event object for the proper structure if provided */ - if (this._events) { - if ((typeof this._events) !== 'object') { - throw new Error("JSDO: JSDO constructor event object is not defined as an object"); - } - - /* make sure all the event handlers are sane */ - for (var prop in this._events) { - var evt = this._events[prop]; - if (!(evt instanceof Array)) { - throw new Error('JSDO: JSDO constructor event object for ' + prop + ' must be an array'); - } - evt.forEach(function (el) { - if ((typeof el) !== 'object') { - throw new Error("JSDO: JSDO constuctor event object for " + - prop + " is not defined as an object"); - } - /* listener must have at least fn property defined as a function */ - if ((typeof el.fn) !== 'function') { - throw new Error("JSDO: JSDO event listener for " + prop + " is not a function."); - } - /* scope is optional, but must be an object if provided */ - if (el.scope && (typeof el.scope) !== 'object') { - throw new Error("JSDO: JSDO event listener scope for " + prop + " is not an object."); - } - }); - } - } - - if (this.name) { - // Read resource definition from the Catalog - save reference to JSDO - // Enhance this to deal with multiple services loaded and the same resource - // name is used by more than one service (use the local serviceName var) - this._resource = progress.data.ServicesManager.getResource(this.name); - if (this._resource) { - if (!this.url) - this.url = this._resource.url; - if (!this._dataSetName && this._resource._dataSetName) { - // Catalog defines a DataSet - this._dataSetName = this._resource._dataSetName; - - // Define TableRef property in the JSDO - if (this._resource.dataProperty) { - var buffer = this[this._resource.dataProperty] - = new progress.data.JSTableRef(this, this._resource.dataProperty); - this._buffers[this._resource.dataProperty] = buffer; - } - else { - for (var tableName in this._resource.fields) { - var buffer = this[tableName] - = new progress.data.JSTableRef(this, tableName); - this._buffers[tableName] = buffer; - } - } - } - if (!this._dataProperty && this._resource.dataProperty) - this._dataProperty = this._resource.dataProperty; - - if (!this._dataSetName) { - var tableName = this._dataProperty ? this._dataProperty : ""; - this._buffers[tableName] = new progress.data.JSTableRef(this, tableName); - if (tableName) - this[tableName] = this._buffers[tableName]; - } - - // Add functions for operations to JSDO object - for (var fnName in this._resource.fn) { - this[fnName] = this._resource.fn[fnName]["function"]; - } - // Check if CUD operations have been defined - this._hasCUDOperations = - this._resource.generic["create"] !== undefined - || this._resource.generic["update"] !== undefined - || this._resource.generic["delete"] !== undefined; - this._hasSubmitOperation = this._resource.generic["submit"] !== undefined; - - /* get a session object, using name of the service to look it up in the list of - * sessions maintained by the ServicesManager - */ - if (!this._session) { - var myservice = progress.data.ServicesManager.getService(this._resource.service.name); - this._session = myservice._session; - this._session._pushJSDOs(this); - } - } - else { - throw new Error(msg.getMsgText("jsdoMSG004", this.name)); - } - } - else { - this._buffers[""] = new progress.data.JSTableRef(this, ""); - } - - if (!this._session) { - throw new Error("JSDO: Unable to get user session for resource '" + this.name + "'"); - } - - // Calculate _numBuffers and _defaultTableRef - for (var buf in this._buffers) { - this._buffers[buf]._parent = null; - this._buffers[buf]._children = []; - // The _relationship object is only specified for the child buffer. - // Currently it is limited to only a single relationship. ie. It does not support the - // where the child buffer is involved in more than one data-relation - this._buffers[buf]._relationship = null; - this._buffers[buf]._isNested = false; - if (!this._defaultTableRef) - this._defaultTableRef = this._buffers[buf]; - this._numBuffers++; - } - if (this._numBuffers != 1) - this._defaultTableRef = null; - else { - // record is used to represent the current record for a table reference - // data corresponds to the values (JSON object) of the data - this.record = null; - } - - // Define caseSensitive property at the JSDO level - if ((typeof Object.defineProperty) == 'function') { - this._caseSensitive = false; // caseSensitive is false by default - Object.defineProperty( - this, - "caseSensitive", - { - get: function () { - return this._caseSensitive; - }, - set: function (value) { - this._caseSensitive = value ? true : false; - - for (var buf in this._buffers) { - this._buffers[buf].caseSensitive = this._caseSensitive; - } - }, - enumerable: true, - writeable: true - }); - this._autoSort = true; // autoSort is true by default - Object.defineProperty( - this, - "autoSort", - { - get: function () { - return this._autoSort; - }, - set: function (value) { - this._autoSort = value ? true : false; - - for (var buf in this._buffers) { - this._buffers[buf].autoSort = this._autoSort; - } - }, - enumerable: true, - writeable: true - }); - } - - // Define _properties property at the JSDO level - this._properties = {}; - if ((typeof Object.defineProperty) == 'function') { - Object.defineProperty( this, - "this._properties", - { - get: function () { - return this._properties; - }, - enumerable: false - } - ); - - } - - - // Set schema for TableRef - if (this._resource && this._resource.fields) { - for (var buf in this._buffers) { - this._buffers[buf]._schema = this._resource.fields[buf]; - this._buffers[buf]._primaryKeys = this._resource.primaryKeys[buf]; - - // Create _fields object used to validate fields as case-insensitive. - this._buffers[buf]._fields = {}; - var fields = this._buffers[buf]._schema; - for (var i = 0; i < fields.length; i++) { - this._buffers[buf]._fields[fields[i].name.toLowerCase()] = fields[i]; - if (typeof(fields[i].origName) !== "undefined") { - if ((typeof(fields[i].origName) !== "string") - || (fields[i].origName.trim() === "")) { - throw new Error(msg.getMsgText("jsdoMSG504", - "JSDO", "Field '" + fields[i].name + "' in resource '" + this._resource.name + "'", "origName")); - } - } - } - - if (this._buffers[buf]._schema && (typeof Object.defineProperty) == 'function') { - // Add fields as properties of the TableRef object - for (var i = 0; i < this._buffers[buf]._schema.length; i++) { - var fieldName = this._buffers[buf]._schema[i].name, - fieldInfo = this._buffers[buf]._schema[i]; - if (typeof(this._buffers[buf][fieldName]) == 'undefined') { - this._defineProperty(buf, fieldName); - } - if (fieldInfo.type === "array") { - for (var j = 0; j < fieldInfo.maxItems; j += 1) { - var name = fieldName + progress.data.JSDO.ARRAY_INDEX_SEPARATOR + (j + 1); - // Skip element if a field with the same name exists - // Only create property if the name is not being used - if (!this._buffers[buf]._fields[name.toLowerCase()]) { - this._defineProperty(buf, name); - } - } - } - } - } - } - // Set schema for when dataProperty is used but not specified via the catalog - if (this._defaultTableRef - && !this._defaultTableRef._schema - && this._resource.fields[""]) { - this._defaultTableRef._schema = this._resource.fields[""]; - } - } - else { - if (this._defaultTableRef) - this._defaultTableRef._schema = []; - } - - // Set isNested property - if (this._numBuffers > 1) { - for (var buf in this._buffers) { - var fields = []; - var found = false; - for (var i = 0; i < this._buffers[buf]._schema.length; i++) { - var field = this._buffers[buf]._schema[i]; - - if (field.items - && field.type == "array" && field.items.$ref) { - if (this._buffers[field.name]) { - found = true; - this._buffers[field.name]._isNested = true; - } - } - else - fields.push(field); - } - // Replace list of fields - removing nested datasets from schema - if (found) - this._buffers[buf]._schema = fields; - } - } - - // Process relationships - if (this._resource && this._resource.relations) { - for (var i = 0; i < this._resource.relations.length; i++) { - var relationship = this._resource.relations[i]; - - // Set relationship information ignoring self-referencing (recursive) relationships - if (relationship.childName - && relationship.parentName - && (relationship.childName !== relationship.parentName)) { - // Set casing of fields in relationFields to be the same as in the schema - if (relationship.relationFields instanceof Array) { - for (var j = 0; j < relationship.relationFields.length; j++) { - var fieldName; - var field; - if (this._buffers[relationship.parentName]._fields) { - fieldName = relationship.relationFields[j].parentFieldName; - field=this._buffers[relationship.parentName]._fields[fieldName.toLowerCase()]; - if (field) { - relationship.relationFields[j].parentFieldName = field.name; - } - else - throw new Error(msg.getMsgText("jsdoMSG010", fieldName)); - } - if (this._buffers[relationship.childName]._fields) { - fieldName = relationship.relationFields[j].childFieldName; - field=this._buffers[relationship.childName]._fields[fieldName.toLowerCase()]; - if (field) { - relationship.relationFields[j].childFieldName = field.name; - } - else - throw new Error(msg.getMsgText("jsdoMSG010", fieldName)); - } - } - } - this._buffers[relationship.childName]._parent = relationship.parentName; - this._buffers[relationship.childName]._relationship = relationship.relationFields; - this._buffers[relationship.parentName]._children.push(relationship.childName); - } - } - } - - this._getDefaultValue = function (field) { - var defaultValue, - t, m, d, - isDate = false; - - if ((field.type === "string") - && field.format - && (field.format.indexOf("date") !== -1) - && (field["default"])) { - isDate = true; - } else if ((field.type === "array") - && field.ablType - && (field.ablType.indexOf("DATE") != -1) - && (field["default"])) { - isDate = true; - } else { - defaultValue = field["default"]; - } - - if (isDate) { - switch (field["default"].toUpperCase()) { - case "NOW": - defaultValue = new Date().toISOString(); - break; - case "TODAY": - t = new Date(); - m = String((t.getMonth() + 1)); - if (m.length === 1) { - m = '0' + m; - } - d = String((t.getDate())); - if (d.length === 1) { - d = '0' + d; - } - defaultValue = t.getFullYear() + '-' + m + '-' + d; - break; - default: - defaultValue = field["default"]; - } - } - - return defaultValue; - }; - - // Method to calculate the element information of an array given the name, index, and value - // Parameters: - // arrayFieldName The name o the field - // index Optional parameter - if index is null/undefined the name of the element is the prefix - // value Optional parameter - this._getArrayField = function (arrayFieldName, index, value) { - var element = {}; - // ABL arrays are 1-based - element.name = arrayFieldName + progress.data.JSDO.ARRAY_INDEX_SEPARATOR + ((index >= 0) ? (index + 1) : ""); - element.value = value ? value[index] : undefined; - return element; - }; - - this.isDataSet = function () { - return this._dataSetName ? true : false; - }; - - /* handler for invoke operation complete */ - this._invokeComplete = function (jsdo, success, request) { - // only fire on async requests - if (request.async && request.fnName) { - jsdo.trigger('afterInvoke', request.fnName, jsdo, success, request); - } - - if (request.deferred) { - if (success) { - request.deferred.resolve(jsdo, success, request); - } - else { - request.deferred.reject(jsdo, success, request); - } - } - }; - - /* handler for invoke operation success */ - this._invokeSuccess = function (/* jsdo, success, request */) { - // do nothing - }; - - /* handler for invoke operation error */ - this._invokeError = function (/* jsdo, success, request */) { - // do nothing - }; - - /* - * Performs an HTTP request using the specified parameters. This is - * used to perform remote calls for the JSDO for operations defined. - * - */ - this._httpRequest = function (xhr, method, url, reqBody, request) { - - function afterOpenRequest() { - var input = null; - if (reqBody) { - xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); - input = JSON.stringify(reqBody); - } - - try { - xhr.send(input); - } catch (e) { - request.success = false; - request.exception = e; - // let Session check for online/offline - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - } - } - - // if xhr wasn't passed we'll create our own since this is an invoke operation - // if xhr is passed, then it is probably a CRUD operation which is setup with XHR - // in call to session - if (!xhr) { - xhr = new XMLHttpRequest(); - - // only setup the callback handlers if we're responsible for creating the - // xhr call which happens on invoke operations...which is the normal case - // the CRUD operations setup their own callbacks and they have their own - // event handlers so we don't use them here. - xhr.onCompleteFn = this._invokeComplete; - xhr.onSuccessFn = this._invokeSuccess; - xhr.onErrorFn = this._invokeError; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - - // for invokes we always fire the invoke when doing async - if (request.async && request.fnName) { - this.trigger('beforeInvoke', request.fnName, this, request); - } - - // For Invoke operations, wrap reqBody in a request object - // This is not required for CRUD operations since the whole - // reqBody is mapped to the parameter - if (reqBody) { - if (this._resource && this._resource.service) { - var useRequest = this._resource.service.useRequest; - if (this._resource.service.settings - && this._resource.service.settings.useRequest !== undefined) { - useRequest = this._resource.service.settings.useRequest; - } - if (useRequest) { - reqBody = {request: reqBody}; - } - } - } - } - - xhr.request = request; - xhr.jsdo = this; - request.jsdo = this; - request.xhr = xhr; - - this._session._openRequest(xhr, method, url, request.async, afterOpenRequest); - - return request; // Note: for the async case, this does not give us exactly the same behavior - // as when afterOpenRequest is called synchronously, because this returns - // request before its xhr has had its open() called - }; - - - // This method currently is just used by the JSDOReadService. - // It returns data in its non-nested (default) format - this._getDataObject = function () { - var dataObject = {}; - if (this._dataSetName) { - dataObject[this._dataSetName] = {}; - - var oldUseRelationships = this.useRelationships; - // Turn off useRelationships so that getData() returns all the records - try { - this.useRelationships = false; - for (var buf in this._buffers) { - dataObject[this._dataSetName][buf] = this._buffers[buf].getData(); - } - } - finally { - // Restore useRelationships - this.useRelationships = oldUseRelationships; - } - } - else { - if (this._dataProperty) { - dataObject[this._dataProperty] = this.getData(); - } - else - return this.getData(); // Array - } - return dataObject; - }; - - - // This method currently is just used by the JSDOReadService. - // Now that the JSDO Services support nested data, we want to return data nested for those - // relationships that are marked nested. - // - // This method returns a data object containing the nested data. - // If a parent row is involved in nested relationship, - // then references to its child rows are added to the parent row in a child table array - // (providing the nested format). - // We are using the internal jsdo _data arrays, - // and adding a child table array to each parent row that has children. - // Once the caller is done with the nested data, - // they can call jsdo._unnestData() which removes these child table references - // - this._getDataObjectAsNested = function () { - var dataObject = {}; - if (this._dataSetName) { - dataObject[this._dataSetName] = {}; - - try { - // First walk thru all buffers. We need to determine if any of the buffers are - // involved in a nested relationship. If so, we want to return the child's - // data in nested format. - for (var buf in this._buffers) { - var bufObj = this._buffers[buf]; - - - // If this is a child table, and its involved in a nested relationship, - // then just skip. - // This table's data will be nested within each parent row when we - // process the parent table. - if (bufObj._isNested) continue; - - this._nestChildren = false; // default to false - - // If table has children, see if any relationship is NESTED - if (bufObj._children.length > 0) { - for (var i = 0; i < bufObj._children.length; i++) { - var childBufObj = this._buffers[bufObj._children[i]]; - - if (childBufObj._isNested) { - this._nestChildren = true; - break; - } - } - } - - dataObject[this._dataSetName][buf] = this._buffers[buf].getData(); - } - } - catch (e) { - throw new Error(msg.getMsgText("jsdoMSG000", e.message)); - } - finally { - // Set back to default avlue - this._nestChildren = false; - } - } - else { - if (this._dataProperty) { - dataObject[this._dataProperty] = this.getData(); - } - else - return this.getData(); // Array - } - return dataObject; - }; - - - // This method is used in conjunction with _getDataObjectAsNested() in the JSDOReadService. - // _getDataObjectAsNested() adds arrays of child row references to their parent rows. - // Once the JSDOReadService has done its data mapping, we need to remove the references since - // internally the JSDO stores its data in unnested format. - this._unnestData = function () { - - if (this._dataSetName) { - var parentRecord; - var bufObj; - var childBufObj; - - // First walk thru all buffers. We need to determine if any of the buffers are parent - // buffers involved in a nested relationship. If so, then we'll look for any child row arrays - // to delete - for (var buf in this._buffers) { - bufObj = this._buffers[buf]; - - // If we know this table has at least one nested child table, we'll walk thru - // all its rows to determine if the rows have any child row arrays. - // It's more efficient to just walk thru the parent row list once, so we'll - // check for all child row arrays here - - if (bufObj._hasNestedChild()) { - // Now must walk thru the parent rows and delete any child row arrays - for (var i = 0; i < bufObj._data.length; i++) { - parentRecord = bufObj._data[i]; - - for (var j = 0; j < bufObj._children.length; j++) { - childBufObj = this._buffers[bufObj._children[j]]; - - if (parentRecord[childBufObj._name]) { - delete parentRecord[childBufObj._name]; - } - } - - } - } - } // end for - } - }; - - - this._recToDataObject = function (record, includeChildren) { - if (this._defaultTableRef) - return this._defaultTableRef._recToDataObject(record, includeChildren); - throw new Error(msg.getMsgText("jsdoMSG001", "_recToDataObject()")); - }; - - this._recFromDataObject = function (dataObject) { - if (this._defaultTableRef) - return this._defaultTableRef._recFromDataObject(dataObject); - throw new Error(msg.getMsgText("jsdoMSG001", "_recFromDataObject()")); - }; - - this.add = function (obj) { - if (this._defaultTableRef) - return this._defaultTableRef.add(obj); - throw new Error(msg.getMsgText("jsdoMSG001", "add() or create()")); - }; - - // Alias for add() method - this.create = this.add; - - this.hasData = function () { - for (var buf in this._buffers) { - if (this._buffers[this._buffers[buf]._name].hasData()) - return true; - } - return false; - }; - - this.getData = function (params) { - if (this._defaultTableRef) - return this._defaultTableRef.getData(params); - throw new Error(msg.getMsgText("jsdoMSG001", "getData()")); - }; - - this.getSchema = function () { - if (this._defaultTableRef) - return this._defaultTableRef.getSchema(); - throw new Error(msg.getMsgText("jsdoMSG001", "getSchema()")); - }; - - this.findById = function (id) { - if (this._defaultTableRef) - return this._defaultTableRef.findById(id); - throw new Error(msg.getMsgText("jsdoMSG001", "findById()")); - }; - - this._convertType = function (value, type, itemType) { - if ((typeof value != 'string') || (type === null)) return value; - var result = value; - try { - if (type == 'array') { - var result = []; - - value = value.slice(1, value.length - 1); - var elements = value.split(','); - var convertItem = (itemType && (itemType != 'string')); - for (var i = 0; i < elements.length; i++) { - result[i] = convertItem ? this._convertType(elements[i], itemType, null) : elements[i]; - } - } - else if (type == 'integer') { - result = parseInt(value); - } - else if (type == 'number') { - result = parseFloat(value); - } - else { - result = value; - } - } - catch (e) { - throw new Error(msg.getMsgText("jsdoMSG000", - "Error converting string to native type: " + e.message)); - } - return result; - }; - - this.assign = function (values) { - if (this._defaultTableRef) { - return this._defaultTableRef.assign(values); - } - else - throw new Error(msg.getMsgText("jsdoMSG001", "assign() or update()")); - }; - - // Alias for assign() method - this.update = this.assign; - - this.remove = function () { - if (this._defaultTableRef) { - return this._defaultTableRef.remove(); - } - else - throw new Error(msg.getMsgText("jsdoMSG001", "remove()")); - }; - - this.getId = function () { - if (this._defaultTableRef) - return this._defaultTableRef.getId(); - throw new Error(msg.getMsgText("jsdoMSG001", "getId()")); - }; - - // getErrors() - JSDO - this.getErrors = function () { - if (this._defaultTableRef) - return this._defaultTableRef.getErrors(); - throw new Error(msg.getMsgText("jsdoMSG001", "getErrors()")); - }; - - this.getErrorString = function () { - if (this._defaultTableRef) - return this._defaultTableRef.getErrorString(); - throw new Error(msg.getMsgText("jsdoMSG001", "getErrorString()")); - }; - - /* - * Finds a record in the JSDO memory using the specified function to determine the record. - */ - this.find = function (fn) { - if (this._defaultTableRef) - return this._defaultTableRef.find(fn); - throw new Error(msg.getMsgText("jsdoMSG001", "find()")); - }; - - this.foreach = function (fn) { - if (this._defaultTableRef) - return this._defaultTableRef.foreach(fn); - throw new Error(msg.getMsgText("jsdoMSG001", "foreach()")); - }; - - this.setSortFields = function (sortFields) { - if (this._defaultTableRef) - return this._defaultTableRef.setSortFields(sortFields); - throw new Error(msg.getMsgText("jsdoMSG001", "setSortFields()")); - }; - - this.setSortFn = function (fn) { - if (this._defaultTableRef) - return this._defaultTableRef.setSortFn(fn); - throw new Error(msg.getMsgText("jsdoMSG001", "setSortFn()")); - }; - - this.sort = function (arg1) { - if (this._defaultTableRef) - return this._defaultTableRef.sort(arg1); - throw new Error(msg.getMsgText("jsdoMSG001", "sort()")); - }; - - this._clearErrors = function () { - this._lastErrors = []; - for (var buf in this._buffers) { - this._buffers[buf]._lastErrors = []; - } - }; - - /** - * setAllRecordsRejected - * - * Sets _allRecordsRejected flag to indicate whether all records have been rejected - * in a saveChanges() call. - * If changes are specified as an array, the changes are used to calculate the flag. - * - * @param {*} param - Array with changes or boolean with value - */ - this._setAllRecordsRejected = function (param) { - var changes, - hasErrors, - hasRejected, - hasCommittedRecords, - i; - - // Note: This function is a single one-stop convenient function to set - // _allRecordsRejected and _someRecordsRejected. - // This logic can be optimized by setting the flags while processing the response. - if (param instanceof Object) { - if (param instanceof Array) { - changes = param; - hasErrors = false; - - this._allRecordsRejected = false; - this._someRecordsRejected = false; - - for (var buf in this._buffers) { - if (this._buffers[buf]._lastErrors.length > 0) { - hasErrors = true; - } - } - if (hasErrors) { - this._allRecordsRejected = true; - this._someRecordsRejected = true; - - for (i = 0; i < changes.length; i += 1) { - if (changes[i].record && !changes[i].record.data._rejected) { - this._allRecordsRejected = false; - return; - } - } - } else if (changes.length > 0) { - this._allRecordsRejected = true; - this._someRecordsRejected = false; - hasCommittedRecords = false; - - for (i = 0; i < changes.length; i += 1) { - if (changes[i].record) { - if (changes[i].record.data._rejected) { - this._someRecordsRejected = true; - } else { - hasCommittedRecords = true; - } - } - } - if (hasCommittedRecords && !this._someRecordsRejected) { - this._allRecordsRejected = false; - } - } - } else { - if (param.operations instanceof Array) { - if (param.operations.length > 0 - && !param.operations[0].success) { - // First operation failed - this._allRecordsRejected = true; - this._someRecordsRejected = true; - - for (i = 0; i < param.operations.length; i += 1) { - if (param.operations[i].success) { - this._allRecordsRejected = false; - return; - } - } - } else { - // Not all operations were rejected - this._allRecordsRejected = false; - this._someRecordsRejected = false; - - for (i = 0; i < param.operations.length; i += 1) { - if (!param.operations[i].success) { - this._someRecordsRejected = true; - return; - } - } - } - } - } - } else { - // Possible values: true, false, undefined - this._allRecordsRejected = param; - this._someRecordsRejected = param; - } - }; - - /* - * Loads data from the HTTP resource. - */ - this.fill = function () { - var objParam, - promise, - properties, - mapping; - - // Clear errors before sending request - this._clearErrors(); - - // Reset _allRecordsRejected - this._setAllRecordsRejected(undefined); - - // Process parameters - if (arguments.length !== 0) { - // Call to fill() has parameters - if (typeof(arguments[0]) == 'function') { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "fill() or read()")); - } - - properties = this.getMethodProperties("read"); - - // Get plugin if mappingType is not undefined, null, or "" - if (properties && properties.mappingType) { - mapping = progress.data.PluginManager.getPlugin(properties.mappingType); - if (!mapping) { - throw new Error(msg.getMsgText("jsdoMSG118", properties.mappingType)); - } - } - - // fill( string); - var filter; - if (arguments[0] === null || arguments[0] === undefined) { - filter = ""; - } - else if (typeof(arguments[0]) == "string") { - filter = arguments[0]; - objParam = {filter: filter}; - } - else if (typeof(arguments[0]) == "object") { - // options - // ablFilter, id, top, skip, sort - - // Use plugin if mappingType is not undefined, null, or "" - if (mapping) { - if (typeof(mapping.requestMapping) === "function") { - objParam = mapping.requestMapping(this, arguments[0], { operation: "read" }); - } - else { - objParam = arguments[0]; - } - } - else { - if (properties.capabilities) { - throw new Error(msg.getMsgText("jsdoMSG119")); - } - objParam = arguments[0]; - } - } - else { - throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "fill() or read()")); - } - } - else { - // fill(); - objParam = null; - } - - var xhr = new XMLHttpRequest(); - var request = { - xhr: xhr, - jsdo: this, - objParam: objParam - }; - - xhr.request = request; - xhr.jsdo = this; - - xhr.onSuccessFn = this._fillSuccess; - xhr.onErrorFn = this._fillError; - xhr.onCompleteFn = this._fillComplete; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - - this.trigger("beforeFill", this, request); - - if (this._resource) { - if (typeof(this._resource.generic.read) == "function") { - xhr.objParam = objParam; - this._resource.generic.read.call(this, xhr, this._async); - if (xhr.request.deferred) { - promise = xhr.request.deferred.promise(); - } - } - else { - throw new Error("JSDO: READ operation is not defined."); - } - } - else { - // Old approach to call READ - this._session._openRequest(xhr, 'GET', this.url, this._async); - try { - xhr.send(null); - } - catch (e) { - request.exception = e; - // get the Client Context ID (AppServer ID) - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - } - } - - // This is the scenario where the read.call did not reach server. i.e., - // some problem in between making successful call to server and we are - // completing the fill() operation with necessary cleanup operations - if (request.success == false && request.exception) { - if ((typeof xhr.onErrorFn) == 'function') { - xhr.onErrorFn(xhr.jsdo, request.success, request); - } - - // get the Client Context ID (AppServer ID) - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - - if ((typeof xhr.onCompleteFn) == 'function') { - xhr.onCompleteFn(xhr.jsdo, request.success, request); - } - } - - return promise; - }; - - // Alias for fill() method - this.read = this.fill; - - /* - * Clears all data (including any pending changes) for each buffer in JSDO - */ - this._clearData = function () { - for (var buf in this._buffers) { - this._buffers[buf]._clearData(); - } - }; - - /* - * Executes a CRUD operation using the built-in API. - */ - this._execGenericOperation = function (operation, objParam, request, - onCompleteFn, onSuccessFn, onErrorFn) { - - var xhr = new XMLHttpRequest(); - request.xhr = xhr; - request.jsdo = this; - request.objParam = objParam; - request.operation = operation; - xhr.jsdo = this; - xhr.onCompleteFn = onCompleteFn; - xhr.onSuccessFn = onSuccessFn; - xhr.onErrorFn = onErrorFn; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - xhr.request = request; - - this._convertRequestData(objParam); - - var operationStr; - switch (operation) { - case progress.data.JSDO._OP_READ: - case progress.data.JSDO._OP_CREATE: - case progress.data.JSDO._OP_UPDATE: - case progress.data.JSDO._OP_DELETE: - case progress.data.JSDO._OP_SUBMIT: - operationStr = PROGRESS_JSDO_OP_STRING[operation]; - break; - default: - throw new Error("JSDO: Unexpected operation " + operation + " in HTTP request."); - } - - if (this._resource) { - if (typeof(this._resource.generic[operationStr]) == "function") { - xhr.objParam = objParam; - this._resource.generic[operationStr](xhr, this._async); - } - else { - // "JSDO: {1} operation is not defined." - throw new Error(msg.getMsgText("jsdoMSG046", operationStr.toUpperCase() )); - } - } - }; - - // Determines if any fields need a conversion when data sent to backend - this._initConvertForServer = function () { - var i, buf, schema; - - // If set, we're good. Field lists for conversion have already been created - if (this._convertForServer !== undefined) { - return; - } - - this._convertForServer = false; - for (buf in this._buffers) { - schema = this._buffers[buf].getSchema(); - this._buffers[buf]._convertFieldsForServer = []; - this._buffers[buf]._convertForServer = false; - - // Check if any fields need conversion - for (i = 0; i < schema.length; i++) { - if (schema[i].ablType && this._ablTypeNeedsConversion(schema[i].ablType)) { - this._buffers[buf]._convertFieldsForServer.push({name: schema[i].name, - ablType: schema[i].ablType}); - } - } - if (this._buffers[buf]._convertFieldsForServer.length > 0) { - this._convertForServer = true; - this._buffers[buf]._convertForServer = true; - } - } - }; - - this._convertRequestData = function (objParam) { - var buf, - beforeData; - - if (this._convertForServer === false) { - return; - } - - // We know at least one table has a field to convert - for (buf in this._buffers) { - if (this._buffers[buf]._convertForServer) { - if (objParam[this._dataSetName]) { - // First convert after-table - if (objParam[this._dataSetName][buf]) { - this._convertTableData(this._buffers[buf], objParam[this._dataSetName][buf]); - } - - // Now let's convert before-image data - beforeData = objParam[this._dataSetName]["prods:before"]; - if (beforeData && beforeData[buf]) { - this._convertTableData(this._buffers[buf], beforeData[buf]); - } - } - // This is for case where saveChanges(false) is called with no before-image data - else if (objParam[buf]) { - this._convertTableData(this._buffers[buf], objParam[buf]); - } - } - } - }; - - this._convertTableData = function (tableRef, tableData) { - var i; - - for (i = 0; i < tableData.length; i++) { - this._convertRowData(tableRef, tableData[i]); - } - }; - - this._convertRowData = function (tableRef, record) { - var i, - field; - - for (i = 0; i < tableRef._convertFieldsForServer.length; i += 1) { - field = tableRef._convertFieldsForServer[i]; - record[field.name] = this._convertField(record[field.name], field.ablType); - } - }; - - this._convertField = function (value, ablType) { - var result; - - if (value === undefined || value === null) { - return value; - } - - if (value instanceof Array) { - var resultArray = []; - for (var i = 0; i < value.length; i++) { - resultArray[i] = this._convertField(value[i], ablType); - } - return resultArray; - } - - try { - switch (ablType.toUpperCase()) { - case "DATE": - case "DATETIME": - if (typeof value === 'string') { - result = value; - } - else if (value instanceof Date) { - result = this._convertDate(value, ablType.toUpperCase()); - } - else { - throw new Error("Unexpected value for " + ablType.toUpperCase() + "."); - } - break; - default: - result = value; - break; - } - } - catch (e) { - throw new Error(msg.getMsgText("jsdoMSG000", - "Error in _convertField for value: " + value + ". " + e.message)); - } - - return result; - }; - - // Convert Date object to string for DATE and DATETIME ablTypes - // Not necessary to do for DATETIME-TZ since JSON.stringify() will do correct conversion - this._convertDate = function (value, ablType) { - var result = value; - - // DATE format should be in ISO 8601 format yyyy-mm-dd - // DATETIME format should be in ISO 8601 format yyyy-mm-ddThh:mm:ss.sss - if (ablType === "DATE" || ablType === "DATETIME") { - result = progress.util._pad(value.getFullYear(), 4) + '-' + - progress.util._pad(value.getMonth() + 1) + '-' + - progress.util._pad(value.getDate()); - - if (ablType === "DATETIME") { - result = result + "T" + - progress.util._pad(value.getHours()) + ":" + - progress.util._pad(value.getMinutes()) + ":" + - progress.util._pad(value.getSeconds()) + "." + - progress.util._pad(value.getMilliseconds(), 3); - } - } - - return result; - }; - - - this._ablTypeNeedsConversion = function (ablType) { - - var needsConversion = false; - - switch (ablType.toUpperCase()) { - case "DATE": - case "DATETIME": - needsConversion = true; - break; - } - - return needsConversion; - }; - - - - this._undefWorkingRecord = function () { - // Set record property - for (var buf in this._buffers) { - this._buffers[buf]._setRecord(null); - } - }; - - /* - * Saves changes in the JSDO. Save any outstanding changes for CREATES, UPDATE, and DELETEs - */ - this.saveChanges = function (useSubmit) { - var promise, - request; - - if (useSubmit === undefined) { - useSubmit = false; - } - else if (typeof(useSubmit) != 'boolean') { - throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "saveChanges()")); - } - - // _fireCUDTriggersForSubmit() needs to know how saveChanges() was called - this._useSubmit = useSubmit; - - // confirm the availability of the operations required for executing this saveChanges call - // (_checkThatJSDOHasRequiredOperations() throws an error if there's a missing operation, - // which this method deliberately allows to bubble up to the caller) - this._checkThatJSDOHasRequiredOperations(); - - // Don't allow Submit with just a temp-table if autoApplyChanges is true - if ( !this._dataSetName && this._useSubmit && this.autoApplyChanges) { - /* error message: "autoApplyChanges is not supported for submit with a temp-table */ - /* Use jsdo.autoApplyChanges = false." */ - throw new Error(msg.getMsgText("jsdoMSG124")); - } - - // Check if any data being sent to server needs to first be converted - this._initConvertForServer(); - - // Clear errors before sending request - this._clearErrors(); - - // Reset _allRecordsRejected - this._setAllRecordsRejected(undefined); - - request = { - jsdo: this - }; - - this.trigger("beforeSaveChanges", this, request); - - if (useSubmit) { - /* Pass in request object. - * Need to use same request object so before and after saveChanges events - * are in sync in JSDO Submit Service. */ - promise = this._syncDataSetForSubmit(request); - } - else if (this._dataSetName) { - promise = this._syncDataSetForCUD(); - } - else { - promise = this._syncSingleTable(); - } - - return promise; - }; - - /* - * _checkThatJSDOHasRequiredOperations - - This method is intended to be used by the saveChanges() method to determine whether - the JSDO's resource definition includes the operations necessary for executing the - types of changes that are pending in the JSDO. It checks for Submit if saveChanges - was called with useSubmit set to true, otherwise it checks whatever CUD operations are - pending. - The JSDO's internal _useSubmit property must be set correctly before this method - is called - */ - this._checkThatJSDOHasRequiredOperations = function( ) { - var checkedDelete = false, - checkedCreate = false, - checkedUpdate = false, - buf, - tableRef; - - if (!this._hasCUDOperations && !this._hasSubmitOperation) { - throw new Error(msg.getMsgText("jsdoMSG026")); - } - - // Validate the use of Submit - if (this._useSubmit) { - if (!this._hasSubmitOperation) { - // "JSDO: {1} operation is not defined."; - throw new Error(msg.getMsgText("jsdoMSG046", "SUBMIT")); - } - else { - return; - } - } - - if (!this._resource) { - // Need the _resource property to do the validation. If not present, just return - // and let execution run as normal (presumably there will be an error) - return; - } - - // Find the pending operations and make sure they are defined - for (buf in this._buffers) { - - tableRef = this._buffers[buf]; - - if (!checkedDelete && tableRef._deleted.length > 0) { - this._confirmOperationExists( progress.data.JSDO._OP_DELETE ); - checkedDelete = true; - } - - if (!checkedCreate && tableRef._added.length > 0) { - this._confirmOperationExists( progress.data.JSDO._OP_CREATE ); - checkedCreate = true; - } - - if (!checkedUpdate && Object.keys(tableRef._changed).length > 0) { - this._confirmOperationExists( progress.data.JSDO._OP_UPDATE ); - checkedUpdate = true; - } - - if ( checkedDelete && checkedCreate && checkedUpdate ) { - break; - } - } - - }; - - // Determines whether a given operation is defined by the JSDO's resource - // throws an error if it's not defined - this._confirmOperationExists = function(operation) { - var operationStr = PROGRESS_JSDO_OP_STRING[operation]; - if (typeof(this._resource.generic[operationStr]) !== "function") { - // "JSDO: {1} operation is not defined." - throw new Error(msg.getMsgText("jsdoMSG046", operationStr.toUpperCase() )); - } - }; - - this.invoke = function (name, object) { - var request = this[name](object); - if (request.deferred) { - return request.deferred.promise(); - } - - return undefined; - }; - - /* - * Synchronizes changes for a TableRef - * - * @param operation HTTP operation to be performed - * @param tableRef Handle to the TableRef - * @param batch Optional. batch information associated with the sync operation. - * If not specified a new one will be created. Used for saving datasets. - */ - this._syncTableRef = function (operation, tableRef, batch) { - var rowData, - requestData, - jsonObject; - - if (tableRef._visited) return; - tableRef._visited = true; - - //ensure batch object is sane - if (!batch) { - batch = { - operations: [] - }; - } else if (!batch.operations) { - batch.operations = []; - } - - // Before children - // Create parent records before children - switch (operation) { - case progress.data.JSDO._OP_CREATE: - for (var i = 0; i < tableRef._added.length; i++) { - var id = tableRef._added[i]; - var jsrecord = tableRef._findById(id, false); - - if (!jsrecord) continue; - if (tableRef._processed[id]) continue; - tableRef._processed[id] = jsrecord.data; - - rowData = {}; - jsonObject = {}; - - // Make copy of row data, in case we need to convert data for backend.. - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - - if (this.isDataSet()) { - if (this._useBeforeImage("create")) { - jsonObject[this._dataSetName] = {}; - var dataSetObject = jsonObject[this._dataSetName]; - dataSetObject["prods:hasChanges"] = true; - - dataSetObject[tableRef._name] = []; - - // Dont need to send prods:id for create, - // no before table or error table to match - // Dont need to send prods:clientId - since only sending one record - rowData["prods:rowState"] = "created"; - rowData["prods:clientId"] = jsrecord.data._id; - - delete rowData["_id"]; - - dataSetObject[tableRef._name].push(rowData); - } - else { - jsonObject[tableRef._name] = []; - jsonObject[tableRef._name].push(rowData); - } - } - else { - jsonObject = rowData; - } - - - var request = { - operation: operation, - batch: batch, - jsrecord: jsrecord, - jsdo: this - }; - batch.operations.push(request); - - jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); - this.trigger("beforeCreate", this, jsrecord, request); - - this._execGenericOperation( - progress.data.JSDO._OP_CREATE, jsonObject, request, this._createComplete, - this._createSuccess, this._createError); - } - break; - case progress.data.JSDO._OP_UPDATE: - for (var id in tableRef._changed) { - var jsrecord = tableRef._findById(id, false); - - if (!jsrecord) continue; - if (tableRef._processed[id]) continue; - tableRef._processed[id] = jsrecord.data; - - rowData = {}; - jsonObject = {}; - requestData = {}; - - // Make copy of row data, in case we need to convert data for backend.. - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - - var useBeforeImageFormat = false; - if (this.isDataSet()) { - if (this._useBeforeImage("update")) { - useBeforeImageFormat = true; - jsonObject[this._dataSetName] = {}; - var dataSetObject = jsonObject[this._dataSetName]; - dataSetObject["prods:hasChanges"] = true; - dataSetObject[tableRef._name] = []; - - // Dont need to send prods:clientId - since only sending one record - rowData["prods:id"] = jsrecord.data._id; - rowData["prods:rowState"] = "modified"; - rowData["prods:clientId"] = jsrecord.data._id; - delete rowData["_id"]; - - dataSetObject[tableRef._name].push(rowData); - - // Now create before-table data - dataSetObject["prods:before"] = {}; - var beforeObject = dataSetObject["prods:before"]; - beforeObject[tableRef._name] = []; - - var beforeRowData = {}; - // Dont need to send prods:clientId - since only sending one record - beforeRowData["prods:id"] = jsrecord.data._id; - - tableRef._jsdo._copyRecord(tableRef, - tableRef._beforeImage[jsrecord.data._id], beforeRowData); - delete beforeRowData["_id"]; - - beforeObject[tableRef._name].push(beforeRowData); - } - } - - if (!useBeforeImageFormat) { - if (this._resource.service - && this._resource.service.settings - && this._resource.service.settings.sendOnlyChanges) { - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData, - tableRef._beforeImage[jsrecord.data._id]); - - if (this._resource.idProperty) { - requestData[this._resource.idProperty] = - jsrecord.data[this._resource.idProperty]; - } - else { - throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, - " for sendOnlyChanges property")); - } - } - else - requestData = rowData; - - if (this.isDataSet()) { - jsonObject[tableRef._name] = []; - jsonObject[tableRef._name].push(requestData); - } - else { - jsonObject = rowData; - } - } - - var request = { - jsrecord: jsrecord, - operation: operation, - batch: batch, - jsdo: this - }; - batch.operations.push(request); - - jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); - this.trigger("beforeUpdate", this, jsrecord, request); - - this._execGenericOperation( - progress.data.JSDO._OP_UPDATE, jsonObject, request, this._updateComplete, - this._updateSuccess, this._updateError); - } - break; - } - - // Call _syncTableRef on child tables - for (var i = 0; i < tableRef._children.length; i++) { - var childTableName = tableRef._children[i]; - this._syncTableRef( - operation, this._buffers[childTableName], batch); - } - - // After children - // Delete parent records after children - - if (operation == progress.data.JSDO._OP_DELETE) { - for (var i = 0; i < tableRef._deleted.length; i++) { - var id = tableRef._deleted[i]._id; - var jsrecord = tableRef._deleted[i]; - - if (!jsrecord) continue; - tableRef._processed[id] = jsrecord.data; - - rowData = {}; - jsonObject = {}; - requestData = {}; - - // Make copy of row data, in case we need to convert data for backend.. - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - - var useBeforeImageFormat = false; - if (this.isDataSet()) { - if (this._useBeforeImage("delete")) { - useBeforeImageFormat = true; - jsonObject[this._dataSetName] = {}; - var dataSetObject = jsonObject[this._dataSetName]; - dataSetObject["prods:hasChanges"] = true; - - // There is no after tables for deletes, so just create before-table data - dataSetObject["prods:before"] = {}; - var beforeObject = dataSetObject["prods:before"]; - beforeObject[tableRef._name] = []; - - var beforeRowData = {}; - - // Dont need to send prods:id for delete, no after table or error table to match - // Dont need to send prods:clientId - since only sending one record - beforeRowData["prods:rowState"] = "deleted"; - beforeRowData["prods:clientId"] = jsrecord.data._id; - - tableRef._jsdo._copyRecord(tableRef, - tableRef._beforeImage[rowData._id], beforeRowData); - beforeObject[tableRef._name].push(beforeRowData); - } - } - - if (!useBeforeImageFormat) { - if (this._resource.service - && this._resource.service.settings - && this._resource.service.settings.sendOnlyChanges) { - if (this._resource.idProperty) { - requestData[this._resource.idProperty] = - jsrecord.data[this._resource.idProperty]; - } - else { - throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, - " for sendOnlyChanges property")); - } - } - else { - requestData = rowData; - } - - if (this.isDataSet()) { - jsonObject[tableRef._name] = []; - jsonObject[tableRef._name].push(requestData); - } - else { - jsonObject = rowData; - } - } - - var request = { - batch: batch, - jsrecord: jsrecord, - operation: operation, - jsdo: this - }; - - batch.operations.push(request); - - jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); - this.trigger("beforeDelete", this, jsrecord, request); - - this._execGenericOperation( - progress.data.JSDO._OP_DELETE, jsonObject, request, this._deleteComplete, - this._deleteSuccess, this._deleteError); - } - } - }; - - /* - * Returns true if the specified operation type was specified in the catalog as useBeforeImage, - * else it returns false. - */ - this._useBeforeImage = function (opType) { - - for (var idx = 0; idx < this._resource.operations.length; idx++) { - if (this._resource.operations[idx].type == opType) { - return this._resource.operations[idx].useBeforeImage; - } - } - - return false; - }; - - - /* - * Synchronizes changes for a DataSet. This is called when we send over one row at at time - * to Create, Update and Delete methods. - * It handles row with or without before-image data. - */ - this._syncDataSetForCUD = function () { - var batch = { - operations: [] - }, - deferred, - promise; - - if (typeof($) == 'function' && typeof($.Deferred) == 'function') { - deferred = $.Deferred(); - promise = deferred.promise(); - batch.deferred = deferred; - } - - // Process buffers - // Synchronize deletes - for (var buf in this._buffers) { - this._buffers[buf]._visited = false; - } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._syncTableRef( - progress.data.JSDO._OP_DELETE, tableRef, batch); - } - - // Synchronize adds - for (var buf in this._buffers) { - this._buffers[buf]._visited = false; - } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._syncTableRef( - progress.data.JSDO._OP_CREATE, tableRef, batch); - } - - // Synchronize updates - for (var buf in this._buffers) { - this._buffers[buf]._visited = false; - } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._syncTableRef( - progress.data.JSDO._OP_UPDATE, tableRef, batch); - } - - if (this.autoApplyChanges) { - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - tableRef._processed = {}; - tableRef._added = []; - tableRef._changed = {}; - tableRef._deleted = []; - } - } - - // OE00229270 If _async is false, this ensures that afterSaveChanges() is called just once - // We now do this after all operations have been processed - if (!this._async) { - if (this._isBatchComplete(batch)) { - var success = this._isBatchSuccess(batch); - var request = { - batch: batch, - success: success - }; - this._undefWorkingRecord(); - - // Save error messages - this._lastErrors = []; - if (!success && batch.operations) { - this._updateLastErrors(this, batch, null); - } - this._setAllRecordsRejected(batch); - - this._fireAfterSaveChanges(success, request); - } - } - // end OE00229270 - - return promise; - }; - - - /* - * Synchronizes changes for a single table - */ - this._syncSingleTable = function () { - var deferred, promise; - if (!this._defaultTableRef) return; - var tableRef = this._defaultTableRef; - - var batch = { - operations: [] - }; - - if (typeof($) == 'function' && typeof($.Deferred) == 'function') { - deferred = $.Deferred(); - promise = deferred.promise(); - batch.deferred = deferred; - } - - var fireAfterSaveChanges = false; - - // Skip delete for records that were added - // mark them as processed - var addedRecords = {}; - for (var i = 0; i < tableRef._added.length; i++) { - var id = tableRef._added[i]; - addedRecords[id] = id; - } - for (var i = 0; i < tableRef._deleted.length; i++) { - var jsrecord = tableRef._deleted[i]; - if (!jsrecord) continue; - - var id = jsrecord.data._id; - if (addedRecords[id]) { - // Set request object - // Properties async, fnName, objParam, and response - // are not set when the HTTP request is suppressed - var request = { - success: true, - xhr: undefined, - operation: progress.data.JSDO._OP_DELETE, - batch: batch, - jsrecord: jsrecord, - jsdo: this - }; - batch.operations.push(request); - tableRef._processed[id] = jsrecord.data; - - var jsdo = request.jsdo; - try { - request.jsrecord._tableRef.trigger("afterDelete", jsdo, request.jsrecord, - request.success, request); - jsdo.trigger("afterDelete", jsdo, request.jsrecord, request.success, request); - } finally { - request.complete = true; - } - - fireAfterSaveChanges = true; - } - } - addedRecords = null; - - // Synchronize deletes - for (var i = 0; i < tableRef._deleted.length; i++) { - var jsrecord = tableRef._deleted[i]; - if (!jsrecord) continue; - - var id = jsrecord.data._id; - if (tableRef._processed[id]) continue; - - tableRef._processed[id] = jsrecord.data; - fireAfterSaveChanges = false; - - var xhr = new XMLHttpRequest(); - xhr.jsdo = this; - - var request = { - xhr: xhr, - operation: progress.data.JSDO._OP_DELETE, - batch: batch, - jsrecord: jsrecord, - jsdo: this - }; - batch.operations.push(request); - xhr.onCompleteFn = this._deleteComplete; - xhr.onSuccessFn = this._deleteSuccess; - xhr.onErrorFn = this._deleteError; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - xhr.request = request; - - jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); - this.trigger("beforeDelete", this, jsrecord, request); - - var requestData = {}; - if (this._resource.service - && this._resource.service.settings - && this._resource.service.settings.sendOnlyChanges) { - if (this._resource.idProperty) { - requestData[this._resource.idProperty] = jsrecord.data[this._resource.idProperty]; - } - else { - throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, - " for sendOnlyChanges property")); - } - } - else { - // We must copy record in case _convertRowData() needs to make conversion - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData); - } - - if (tableRef._convertForServer) { - this._convertRowData(tableRef, requestData); - } - - if (this._resource) { - if (typeof(this._resource.generic["delete"]) == "function") { - xhr.objParam = requestData; - this._resource.generic["delete"].call(this, xhr, this._async); - } - else { - throw new Error("JSDO: DELETE operation is not defined."); - } - } - else { - this._session._openRequest(xhr, 'DELETE', this.url + '/' + id, true); - try { - xhr.send(null); - } catch (e) { - request.success = false; - request.exception = e; - // let Session check for online/offline - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - } - - } - } - - // Synchronize adds - for (var i = 0; i < tableRef._added.length; i++) { - var id = tableRef._added[i]; - var jsrecord = tableRef._findById(id, false); - var requestData = {}; - - if (!jsrecord) continue; - if (tableRef._processed[id]) continue; - tableRef._processed[id] = jsrecord.data; - fireAfterSaveChanges = false; - - var xhr = new XMLHttpRequest(); - xhr.jsdo = this; - var request = { - xhr: xhr, - jsrecord: jsrecord, - batch: batch, - operation: progress.data.JSDO._OP_CREATE, - jsdo: this - }; - batch.operations.push(request); - xhr.onCompleteFn = this._createComplete; - xhr.onSuccessFn = this._createSuccess; - xhr.onErrorFn = this._createError; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - xhr.request = request; - - jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); - this.trigger("beforeCreate", this, jsrecord, request); - - if (this._resource) { - if (typeof(this._resource.generic.create) == "function") { - this._copyRecord(tableRef, jsrecord.data, requestData); - if (this._resource.idProperty !== undefined && jsrecord.data._id !== undefined) { - // Remove _id when idProperty is set - delete requestData._id; - } - - if (tableRef._convertForServer) { - this._convertRowData(tableRef, requestData); - } - - xhr.objParam = requestData; - - this._resource.generic.create.call(this, xhr, this._async); - } - else { - throw new Error("JSDO: CREATE operation is not defined."); - } - - } - else { - this._session._openRequest(xhr, 'POST', this.url, true); - xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); - this._copyRecord(tableRef, jsrecord.data, requestData); - - if (tableRef._convertForServer) { - this._convertRowData(tableRef, requestData); - } - var input = JSON.stringify(requestData); - - try { - xhr.send(input); - } catch (e) { - request.success = false; - request.exception = e; - // let Session check for online/offline - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - } - - } - } - - // Synchronize updates - for (var id in tableRef._changed) { - var jsrecord = tableRef._findById(id, false); - - if (!jsrecord) continue; - if (tableRef._processed[id]) continue; - tableRef._processed[id] = jsrecord.data; - fireAfterSaveChanges = false; - - var xhr = new XMLHttpRequest(); - var request = { - xhr: xhr, - jsrecord: jsrecord, - operation: progress.data.JSDO._OP_UPDATE, - batch: batch, - jsdo: this - }; - xhr.request = request; - xhr.jsdo = this; - batch.operations.push(request); - xhr.onCompleteFn = this._updateComplete; - xhr.onSuccessFn = this._updateSuccess; - xhr.onErrorFn = this._updateError; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - - jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); - this.trigger("beforeUpdate", this, jsrecord, request); - - var requestData = {}; - if (this._resource.service - && this._resource.service.settings - && this._resource.service.settings.sendOnlyChanges) { - - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData, - tableRef._beforeImage[jsrecord.data._id]); - - if (this._resource.idProperty) { - requestData[this._resource.idProperty] = jsrecord.data[this._resource.idProperty]; - } - else { - throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, - " for sendOnlyChanges property")); - } - } - else { - // We must copy record in case _convertRowData() needs to make conversion - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData); - } - - if (tableRef._convertForServer) { - this._convertRowData(tableRef, requestData); - } - - if (this._resource) { - if (typeof(this._resource.generic.update) == "function") { - xhr.objParam = requestData; - this._resource.generic.update.call(this, xhr, this._async); - } - else { - throw new Error("JSDO: UPDATE operation is not defined."); - } - } - else { - this._session._openRequest(xhr, 'PUT', this.url + '/' + id, this._async); - xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); - - var input = JSON.stringify(requestData); - - try { - xhr.send(input); - } catch (e) { - request.success = false; - request.exception = e; - // let Session check for online/offline - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - } - } - } - - if (this.autoApplyChanges) { - // Arrays to keep track of changes - tableRef._added = []; - tableRef._changed = {}; - tableRef._deleted = []; - tableRef._processed = {}; - } - - // OE00229270 If _async is false, fire afterSaveChanges() after all operations are processed - if (!this._async) - fireAfterSaveChanges = true; - - if (fireAfterSaveChanges) { - var jsdo = this; - var request = { - batch: batch, - success: true - }; - - // Save error messages - jsdo._lastErrors = []; - if (batch.operations) { - jsdo._updateLastErrors(jsdo, batch, null); - } - - jsdo._undefWorkingRecord(); - jsdo._fireAfterSaveChanges(request.success, request); - } - - return promise; - }; - - /************************************************************************ - * - * Synchronizes changes for a DataSet or a temp-table, sending over the entire change-set - * to saveChanges() on server - * If sync'ing a DataSet, sends over before-image and after-image data. - */ - this._syncDataSetForSubmit = function (request) { - var deferred, - promise, - jsonObject, - completeFn = this._saveChangesComplete, - successFn = this._saveChangesSuccess, - errorFn = this._saveChangesError; - - if (typeof($) == 'function' && typeof($.Deferred) == 'function') { - deferred = $.Deferred(); - promise = deferred.promise(); - request.deferred = deferred; - } - - request.jsrecords = []; - - // First thing to do is to create jsonObject with before and after image data for all - // records in change-set (creates, updates and deletes) - if ( this._dataSetName ) { - jsonObject = this._createChangeSet(this._dataSetName, false, request); - } - else { - // just a temp-table. Need to create it somewhat differently from DS - // (no before and after image data) - jsonObject = this._createTTChangeSet(this._defaultTableRef, request); - successFn = this._saveChangesSuccessTT; // will process success response differently from DS - } - - this._execGenericOperation(progress.data.JSDO._OP_SUBMIT, jsonObject, request, - completeFn, successFn, errorFn); - - return promise; - }; - - /************************************************************************ - * - * Private method that creates a jsonObject with before and after image data for all - * records in change-set (creates, updates and deletes) - * - * Params: dataSetName is required. - * alwaysCreateTable is required. If true, always create table array (even if no data/changes) - * request is optional - */ - this._createChangeSet = function (dataSetName, alwaysCreateTable, request) { - var changeSetJsonObject = {}; - - changeSetJsonObject[dataSetName] = {}; - var dataSetJsonObject = changeSetJsonObject[dataSetName]; - - var hasChanges = dataSetJsonObject["prods:hasChanges"] = this._hasChanges(); - if (hasChanges) { - if ((alwaysCreateTable === true)) { - for (var buf in this._buffers) { - dataSetJsonObject[this._buffers[buf]._name] = []; - } - } - - // First do deletes - //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._addDeletesToChangeSet(tableRef, dataSetJsonObject, request); - } - - // Adds - //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._addCreatesToChangeSet(tableRef, dataSetJsonObject, request); - } - - // Updates - //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._addChangesToChangeSet(tableRef, dataSetJsonObject, request); - } - - // Clear _processed map - for (var buf in this._buffers) { - this._buffers[buf]._processed = {}; - } - } - - // Check if change set is empty - // A saveChanges() with a delete of new record would result in an empty change set - // An empty DataSet is sent to the server to ensure that AfterSaveChanges fires - var keys = Object.keys(changeSetJsonObject[dataSetName]); - if (keys.length == 1 && keys[0] == "prods:hasChanges") { - for (var buf in this._buffers) { - dataSetJsonObject[this._buffers[buf]._name] = []; - } - dataSetJsonObject["prods:hasChanges"] = false; - } - - return changeSetJsonObject; - }; - - /************************************************************************ - * - * Private method that creates a jsonObject for the created and changed records - * in a temp-table. There is no before-image information. This is used in the - * case of a Submit operation when the JSDO is just for a temp-table - * - * Params: dataSetName is required. - * alwaysCreateTable is required. If true, always create table array (even if no data/changes) - * request is optional - */ - this._createTTChangeSet = function (tableRef, request) { - var changeSetJsonObject = {}, - hasChanges, - tempTableJsonObject, - i, - id, - jsrecord; - - changeSetJsonObject[tableRef._name] = []; - tempTableJsonObject = changeSetJsonObject[tableRef._name]; - - hasChanges = this._hasChanges(); - if (hasChanges) { - - // (note that we do not send deleted rows on submit for a temp-table) - - // Adds - for (i = 0; i < tableRef._added.length; i++) { - id = tableRef._added[i]; - jsrecord = tableRef._findById(id, false); - if (jsrecord) { - if ( !tableRef._processed[jsrecord.data._id] ) { - this._addRowToTTChangeSet(tableRef, jsrecord, tempTableJsonObject, - request, "beforeCreate"); - } - } - } - - // changed rows - for (id in tableRef._changed) { - if (tableRef._changed.hasOwnProperty(id)) { - jsrecord = tableRef._findById(id, false); - if (jsrecord) { - if ( !tableRef._processed[jsrecord.data._id] ) { - this._addRowToTTChangeSet(tableRef, jsrecord, tempTableJsonObject, - request, "beforeUpdate"); - } - } - } - } - - // Clear _processed map - tableRef._processed = {}; - } - - return changeSetJsonObject; - }; - - this._addRowToTTChangeSet = function (tableRef, jsrecord, tempTableJsonObject, request, event) { - var rowData = {}; - - tableRef._processed[jsrecord.data._id] = jsrecord.data; - - // Store jsrecord in request object so we can access it when saveChanges completes, - // in order to run afterCreate events - if (typeof(request) != 'undefined') { - request.jsrecords.push(jsrecord); - - // Need to call beforeCreate trigger when saveChanges(true) is called - jsrecord._tableRef.trigger(event, this, jsrecord, request); - this.trigger(event, this, jsrecord, request); - } - - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - delete rowData["_id"]; - - tempTableJsonObject.push(rowData); - }; - - /************************************************************************ - * - * Private method that creates a jsonObject with data and also before image data - * for all records in change-set (creates, updates and deletes) - * - * Params: dataSetName is required. - * It returns jsonObject that can be used as input to addRecords() - */ - this._createDataAndChangeSet = function (dataSetName) { - var jsonObject = {}; - - jsonObject[dataSetName] = {}; - var dataSetJsonObject = jsonObject[dataSetName]; - - /* We always want to create tables (even if there's no data) so we can compare schemas - * of data in local storage to JSDO's schema */ - for (var buf in this._buffers) - dataSetJsonObject[this._buffers[buf]._name] = []; - - if (this._hasChanges()) { - dataSetJsonObject["prods:hasChanges"] = true; - } - - // Add data from each table. This will also add bi data for any created or updated rows - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._addRecordsToObject(tableRef, dataSetJsonObject); - } - - // Now do deletes - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._addDeletesToChangeSet(tableRef, dataSetJsonObject); - } - - // Clear _processed map - for (var buf in this._buffers) { - this._buffers[buf]._processed = {}; - } - return jsonObject; - }; - - // This method adds all record for specified table into dataSetJsonObject. - // If record has bi data, it adds that as well - this._addRecordsToObject = function (tableRef, dataSetJsonObject) { - - if (tableRef._data.length > 0 && !dataSetJsonObject[tableRef._name]) - dataSetJsonObject[tableRef._name] = []; - - for (var i = 0; i < tableRef._data.length; i++) { - var record = tableRef._data[i]; - if (!record) continue; - - // Check if record has bi data, can only determine if it's created or changed since - // deleted rows are not in after data - if (this._doesRecordHaveCreateBIData(tableRef, record._id) === true) { - var jsrecord = tableRef._findById(record._id, false); - if (!jsrecord) continue; - if (tableRef._processed[jsrecord.data._id]) continue; - this._addCreatedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject); - } - if (this._doesRecordHaveUpdateBIData(tableRef, record._id) === true) { - var jsrecord = tableRef._findById(record._id, false); - if (!jsrecord) continue; - if (tableRef._processed[jsrecord.data._id]) continue; - this._addChangedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject); - } - else { - if (tableRef._processed[record._id]) continue; - tableRef._processed[record._id] = record; - - var rowData = {}; - - tableRef._jsdo._copyRecord(tableRef, record, rowData); - delete rowData["_id"]; - - dataSetJsonObject[tableRef._name].push(rowData); - } - } - }; - - - // Check if specified after record has bi data for newly created record. - // Returns True if after record has corresponding bi data, else false - this._doesRecordHaveCreateBIData = function (tableRef, id) { - for (var i = 0; i < tableRef._added.length; i++) { - if (tableRef._added[i] === id) - return true; - } - - return false; - }; - - // Check if specified after record has bi data for updated record. - // Returns True if after record has corresponding bi data, else false - this._doesRecordHaveUpdateBIData = function (tableRef, id) { - for (var changedId in tableRef._changed) { - if (changedId === id) - return true; - } - - return false; - }; - - - // If a create, remove or update exists, method returns true, else returns false - this._hasChanges = function () { - var hasChanges = false; - - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - - var hasUpdates = false; - for (var id in tableRef._changed) { - hasUpdates = true; - break; - } - - if (tableRef._deleted.length > 0 || tableRef._added.length > 0 || hasUpdates) { - hasChanges = true; - break; - } - } - - return hasChanges; - }; - - // This method is used when saveChanges() is called, and also when storing data to local storage. - // The request param should be defined for saveChanges(), - // but not needed when storing data to local storage - this._addDeletesToChangeSet = function (tableRef, dataSetJsonObject, request) { - // There is no after table for deletes, so just create before-table data - for (var i = 0; i < tableRef._deleted.length; i++) { - var jsrecord = tableRef._deleted[i]; - - if (!jsrecord) continue; - - if (jsrecord.data - && jsrecord.data._id !== undefined - && tableRef._beforeImage[jsrecord.data._id] === null) { - // Deleted record is for a new record - do not send deleted record to server - continue; - } - - this._addDeletedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); - } - }; - - this._addDeletedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { - tableRef._processed[jsrecord.data._id] = jsrecord.data; - - // Store jsrecord in request object so we can access it when saveChanges completes, - // in order to run afterDelete events - jsrecord.data["prods:rowState"] = "deleted"; - - if (typeof(request) != 'undefined') { - request.jsrecords.push(jsrecord); - - // Need to call beforeDelete trigger if saveChanges(true) is called - jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); - this.trigger("beforeDelete", this, jsrecord, request); - } - - var beforeRowData = {}; - // AppServer will roundtrip this back to jsdo client - beforeRowData["prods:clientId"] = jsrecord.data._id; - beforeRowData["prods:rowState"] = "deleted"; - - var beforeTableJsonObject = this._getTableInBeforeJsonObject(dataSetJsonObject, tableRef._name); - tableRef._jsdo._copyRecord(tableRef, tableRef._beforeImage[jsrecord.data._id], beforeRowData); - delete beforeRowData["_id"]; - - beforeTableJsonObject.push(beforeRowData); - }; - - // This method is used when saveChanges() is called, and also when storing data to local storage. - // The request param should be defined for saveChanges(), - // but not needed when storing data to local storage - this._addCreatesToChangeSet = function (tableRef, dataSetJsonObject, request) { - // There is no before table for creates, so just create after-table data - for (var i = 0; i < tableRef._added.length; i++) { - var id = tableRef._added[i]; - var jsrecord = tableRef._findById(id, false); - if (!jsrecord) continue; - if (tableRef._processed[jsrecord.data._id]) continue; - - this._addCreatedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); - } - }; - - this._addCreatedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { - tableRef._processed[jsrecord.data._id] = jsrecord.data; - - if (!dataSetJsonObject[tableRef._name]) { - dataSetJsonObject[tableRef._name] = []; - } - - // Store jsrecord in request object so we can access it when saveChanges completes, - // in order to run afterCreate events - jsrecord.data["prods:rowState"] = "created"; - - if (typeof(request) != 'undefined') { - request.jsrecords.push(jsrecord); - - // Need to call beforeCreate trigger when saveChanges(true) is called - jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); - this.trigger("beforeCreate", this, jsrecord, request); - } - - var rowData = {}; - // AppServer will roundtrip this back to jsdo client - rowData["prods:clientId"] = jsrecord.data._id; - rowData["prods:rowState"] = "created"; - - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - delete rowData["_id"]; - - dataSetJsonObject[tableRef._name].push(rowData); - }; - - // This method is used when saveChanges() is called, and also when storing data to local storage. - // The request param should be defined for saveChanges(), - // but not needed when storing data to local storage - this._addChangesToChangeSet = function (tableRef, dataSetJsonObject, request) { - // For Changes, there is both before and after table data - for (var id in tableRef._changed) { - var jsrecord = tableRef._findById(id, false); - if (!jsrecord) continue; - if (tableRef._processed[jsrecord.data._id]) continue; - - this._addChangedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); - } - }; - - this._addChangedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { - tableRef._processed[jsrecord.data._id] = jsrecord.data; - - if (!dataSetJsonObject[tableRef._name]) { - dataSetJsonObject[tableRef._name] = []; - } - - // Store jsrecord in request object so we can access it when saveChanges completes, in order - // to run afterUpdate events - jsrecord.data["prods:rowState"] = "modified"; - - if (typeof(request) != 'undefined') { - request.jsrecords.push(jsrecord); - - // Need to call beforeUpdate trigger when saveChanges(true) is called - jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); - this.trigger("beforeUpdate", this, jsrecord, request); - } - - var rowData = {}; - // Required by AppServer in before-image data. Matches before row - rowData["prods:id"] = jsrecord.data._id; - // AppServer will roundtrip this back to jsdo client - rowData["prods:clientId"] = jsrecord.data._id; - rowData["prods:rowState"] = "modified"; - - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - delete rowData["_id"]; - - dataSetJsonObject[tableRef._name].push(rowData); - - // Now add before-image data - var beforeTableJsonObject = this._getTableInBeforeJsonObject(dataSetJsonObject, tableRef._name); - var beforeRowData = {}; - // Required by AppServer in before-image data. Matches after row - beforeRowData["prods:id"] = jsrecord.data._id; - - tableRef._jsdo._copyRecord(tableRef, tableRef._beforeImage[jsrecord.data._id], beforeRowData); - //delete beforeRowData["_id"]; - - beforeTableJsonObject.push(beforeRowData); - }; - - - // Private method to get table's json object from the specified dataset json object. - // If it hasn't been created yet, this method creates it. - this._getTableInBeforeJsonObject = function (dataSetJsonObject, tableName) { - if (!dataSetJsonObject["prods:before"]) { - dataSetJsonObject["prods:before"] = {}; - } - var beforeObject = dataSetJsonObject["prods:before"]; - - if (!beforeObject[tableName]) { - beforeObject[tableName] = []; - } - - return beforeObject[tableName]; - }; - - - /********************************************************************* - * - * Reads a JSON object into the JSDO memory. - */ - this.addRecords = function (jsonObject, addMode, keyFields, trackChanges, isInvoke) { - if (this.isDataSet()) { - if (jsonObject instanceof Array) { - if (!this._defaultTableRef) { - throw new Error(msg.getMsgText("jsdoMSG998")); - } - } - else { - if (jsonObject === undefined || jsonObject === null) { - jsonObject = {}; - } - - if (jsonObject[this._dataSetName]) { - jsonObject = jsonObject[this._dataSetName]; - } - } - - // Allow empty object in addRecords with MODE_EMPTY - if (addMode != progress.data.JSDO.MODE_EMPTY) { - if (Object.keys(jsonObject).length === 0) - throw new Error(msg.getMsgText("jsdoMSG006")); - } - - var oldUseRelationships = this.useRelationships; - // Turn off useRelationships since addRecords() does not use the working record - this.useRelationships = false; - try { - for (var buf in this._buffers) { - // Read data for tables in JSON object - if (jsonObject[this._buffers[buf]._name]) - this._addRecords(this._buffers[buf]._name, jsonObject, addMode, - keyFields, trackChanges, isInvoke); - else if (addMode == progress.data.JSDO.MODE_EMPTY) { - this._buffers[this._buffers[buf]._name]._clearData(); - } - } - } finally { - // Restore useRelationships - this.useRelationships = oldUseRelationships; - } - } - else if (this._defaultTableRef) { - this._addRecords(this._defaultTableRef._name, jsonObject, addMode, keyFields, - trackChanges, isInvoke); - } - }; - - /* - * Copies the fields of the source record to the target record. - * Preserves the _id of the target record. - */ - this._copyRecord = function (tableRef, source, target, onlyChangesRecord) { - for (var field in source) { - - if (onlyChangesRecord !== undefined) { - if (source[field] == onlyChangesRecord[field]) - continue; - } - - // Fix for PSC00277769 - if (source[field] === undefined || source[field] === null) { - target[field] = source[field]; - } - else if (source[field] instanceof Date) { - target[field] = source[field]; - } - else if (typeof source[field] === 'object') { - var newObject = source[field] instanceof Array ? [] : {}; - this._copyRecord(tableRef, source[field], newObject); - target[field] = newObject; - } - else - target[field] = source[field]; - } - }; - - /* - * Deletes the "prods:" properties when no longer needed, - * typically when doing acceptChanges, rejectChanges, or _applyChanges. - * These properties are used to transfer before-image info between client JSDO and AppServer. - * - * Also, it optionally clears out the errorString field depending upon value of clearErrorString. - * To be consistent with the handling of - * the ABL's Buffer ERROR-STRING attribute, - * the errorString field should be cleared out when doing acceptChanges() or rejectChanges(). - */ - this._deleteProdsProperties = function (record, clearErrorString, deleteRowState) { - - /* Default to false */ - if (typeof(clearErrorString) == 'undefined') { - clearErrorString = false; - } - - /* Default to true */ - if (typeof(deleteRowState) == 'undefined') { - deleteRowState = true; - } - - if (record) { - delete record["prods:id"]; - delete record["prods:hasErrors"]; - delete record["prods:clientId"]; - delete record["prods:rejected"]; - delete record._rejected; - - if (deleteRowState) { - delete record["prods:rowState"]; - } - - if (clearErrorString) { - delete record._errorString; - } - } - }; - - this._addRecords = function (tableName, jsonObject, addMode, keyFields, trackChanges, isInvoke) { - var beforeImageJsonObject = null; - var beforeImageJsonIndex = null; - - if (jsonObject && (this._dataSetName !== undefined)) { - if (jsonObject[this._dataSetName] && - jsonObject[this._dataSetName]["prods:hasChanges"]) { - beforeImageJsonObject = jsonObject; - beforeImageJsonIndex = {}; - } - else if (jsonObject["prods:hasChanges"]) { - beforeImageJsonObject = {}; - beforeImageJsonObject[this._dataSetName] = jsonObject; - beforeImageJsonIndex = {}; - } - } - - if (typeof(tableName) != 'string') - throw new Error(msg.getMsgText("jsdoMSG020")); - if (!addMode) - throw new Error(msg.getMsgText("jsdoMSG021")); - - switch (addMode) { - case progress.data.JSDO.MODE_APPEND: - case progress.data.JSDO.MODE_EMPTY: - case progress.data.JSDO.MODE_MERGE: - case progress.data.JSDO.MODE_REPLACE: - break; - default: - throw new Error(msg.getMsgText("jsdoMSG022")); - } - - if (!keyFields) - keyFields = []; - else { - if (!(keyFields instanceof Array) && (typeof(keyFields) == 'object')) { - if (keyFields[tableName]) { - keyFields = keyFields[tableName]; - } - else { - keyFields = []; - } - } - } - - if (!(keyFields instanceof Array)) { - throw new Error(msg.getMsgText("jsdoMSG008")); - } - - // Check that the specified field names are in the schema - if (this._buffers[tableName]._fields) { - for (var i = 0; i < keyFields.length; i++) { - var field = this._buffers[tableName]._fields[keyFields[i].toLowerCase()]; - if (field === undefined) { - throw new Error(msg.getMsgText("jsdoMSG009", keyFields[i])); - } - else { - keyFields[i] = field.name; - } - } - } - - trackChanges = trackChanges ? true : false; - - if (tableName) { - if (!(jsonObject instanceof Array)) { - var data = null; - - if (jsonObject === undefined || jsonObject === null) { - jsonObject = {}; - } - - if (this.isDataSet()) { - if (jsonObject[this._dataSetName]) - data = jsonObject[this._dataSetName][tableName]; - else if (jsonObject[tableName]) - data = jsonObject[tableName]; - } else { - if (this._dataProperty) - data = jsonObject[this._dataProperty]; - else if (jsonObject.data) - data = jsonObject.data; - } - - - if (data instanceof Array) { - saveJsonObject = jsonObject; - jsonObject = data; - } - else if ((addMode == progress.data.JSDO.MODE_EMPTY) - && (typeof (jsonObject) == 'object') - && (Object.keys(jsonObject).length === 0)) { - jsonObject = []; // Allow empty object in addRecords with - // MODE_EMPTY - } - // Allow empty object when called by restoreChangesOnlyForTable() - // where there are only deletes - in bi data - else if ((addMode == progress.data.JSDO.MODE_REPLACE) - && (typeof (jsonObject) == 'object') - && (beforeImageJsonObject)) { - jsonObject = []; - } - } - - if (!(jsonObject instanceof Array)) { - throw new Error(msg.getMsgText("jsdoMSG005", tableName)); - } - - var dataHasBeenProcessed = false; - try { - this._buffers[tableName]._sortRecords = false; - if (keyFields.length === 0 || addMode == progress.data.JSDO.MODE_EMPTY) { - // Quick merge - if (addMode == progress.data.JSDO.MODE_EMPTY) { - this._buffers[tableName]._clearData(); - } - // APPEND, MERGE, REPLACE - for (var i = 0; i < jsonObject.length; i++) { - var jsrecord = this._buffers[tableName]._add(jsonObject[i], trackChanges, false); - jsonObject[i]._id = jsrecord.data._id; - if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { - beforeImageJsonIndex[jsonObject[i]["prods:id"]] = jsrecord.data._id; - } - if (beforeImageJsonObject) { - this._deleteProdsProperties(jsrecord.data); - } - } - } - else { - // Build temporary index - var tmpIndex; - - if (this._buffers[tableName]._data.length * jsonObject.length >= 10) { - tmpIndex = {}; - - for (var i = 0; i < this._buffers[tableName]._data.length; i++) { - var record = this._buffers[tableName]._data[i]; - if (!record) continue; - - var key = this._buffers[tableName]._getKey(record, keyFields); - tmpIndex[key] = record; - } - - } - else - tmpIndex = null; // Do not use an index - var checkBeforeImage = - (Object.keys(this._buffers[tableName]._beforeImage).length !== 0); - for (var i = 0; i < jsonObject.length; i++) { - var match = false; - var record = null; - - // Check for duplicates - if (tmpIndex) { - var key = this._buffers[tableName]._getKey(jsonObject[i], keyFields); - record = tmpIndex[key]; - match = (record !== undefined); - } - else { - for (var j = 0; j < this._buffers[tableName]._data.length; j++) { - record = this._buffers[tableName]._data[j]; - if (!record) continue; - match = - (this._buffers[tableName]._equalRecord(jsonObject[i], record, keyFields)); - if (match) { - // Duplicate found - break; - } - } - } - - if (match) { - if (isInvoke - && (this._resource.idProperty !== undefined) - && (jsonObject[i]._id === undefined)) { - // Add _id to jsonObject - jsonObject[i]._id = record._id; - } - - // If beforeRecord is null, there is entry in _beforeImage for a create. - // If beforeRecord is undefined, there is no entry - var beforeRecord = this._buffers[tableName]._beforeImage[record._id]; - if (checkBeforeImage - && (jsonObject[i]["prods:id"] !== undefined) - && (typeof beforeRecord !== 'undefined')) { - // Only throw exception if the existing bi data - // is not the same as the new bi data - var isAfterSame = this._sameData(jsonObject[i], record); - var isBeforeSame = true; - - // For creates, beforeRecord will be null - if (beforeRecord) { - var beforeObject = this._getBeforeRecordFromObject(jsonObject[i], - beforeImageJsonObject, tableName); - if (beforeObject) - isBeforeSame = this._sameData(beforeObject, beforeRecord); - } - - if (!isAfterSame || !isBeforeSame) - throw new Error(msg.getMsgText("jsdoMSG032")); - } - - switch (addMode) { - case progress.data.JSDO.MODE_APPEND: - throw new Error(msg.getMsgText("jsdoMSG023")); - case progress.data.JSDO.MODE_MERGE: - /* Ignore duplicate */ - if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { - beforeImageJsonIndex[jsonObject[i]["prods:id"]] = record._id; - } - break; - case progress.data.JSDO.MODE_REPLACE: - if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { - beforeImageJsonIndex[jsonObject[i]["prods:id"]] = record._id; - } - - if (jsonObject[i]._id === undefined) - jsonObject[i]._id = record._id; - this._copyRecord( - this._buffers[tableName], - jsonObject[i], record); - this._deleteProdsProperties(record); - break; - default: - break; - } - } - else { - // Add record - var jsrecord = - this._buffers[tableName]._add(jsonObject[i], trackChanges, false); - jsonObject[i]._id = jsrecord.data._id; - if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { - beforeImageJsonIndex[jsonObject[i]["prods:id"]] = jsrecord.data._id; - } - if (beforeImageJsonObject) { - this._deleteProdsProperties(jsrecord.data); - } - if (tmpIndex) { - var key = this._buffers[tableName]._getKey(jsrecord.data, keyFields); - tmpIndex[key] = jsrecord.data; - } - } - - } - tmpIndex = null; - } - dataHasBeenProcessed = true; - } - finally { - this._buffers[tableName]._sortRecords = true; - this._buffers[tableName]._sort(); - this._buffers[tableName]._createIndex(); - - if (dataHasBeenProcessed && beforeImageJsonObject) { - this._buffers[tableName]._loadBeforeImageData(beforeImageJsonObject, - beforeImageJsonIndex, keyFields); - } - } - } - }; - - // This method returns corresponding bi record of the afterRecord from specified jsonObject - this._getBeforeRecordFromObject = function (afterRecord, jsonObject, tablename) { - var beforeData = jsonObject[this._dataSetName]["prods:before"]; - var id = afterRecord["prods:id"]; - var beforeRecord; - - if (!beforeData) return beforeRecord; - - // First check to see if the before data is the same - for (var i = 0; i < beforeData[tablename].length; i++) { - var record = beforeData[tablename][i]; - if (record["prods:id"] && id == record["prods:id"]) { - beforeRecord = record; - break; - } - } - - return beforeRecord; - }; - - this._sameData = function (record1, record2) { - var value1, value2; - for (var fieldName in record1) { - if (fieldName.substring(0, 5) != "prods" && fieldName != "_id") { - value1 = record1[fieldName]; - value2 = record2[fieldName]; - - if (value1 > value2 || value1 === null) - return false; - else if (value1 < value2 || value2 === null) - return false; - } - } - - return true; - }; - - - // private method to merge changes after a read operation - this._mergeRead = function (jsonObject, xhr) { - if (this.isDataSet()) { - if (this._dataProperty) { - var datasetBuffer = this._buffers[this._dataProperty]; - datasetBuffer._data = jsonObject[this._dataSetName][this._dataProperty]; - if (datasetBuffer.autoSort) { - datasetBuffer._sort(); - } - datasetBuffer._createIndex(); - } - else { - // Load data from JSON object into _data - for (var buf in this._buffers) { - var data; - if (jsonObject[this._dataSetName]) - data = jsonObject[this._dataSetName][buf]; - else - data = null; - data = data ? data : []; - this._buffers[buf]._data = data; - if (this._buffers[buf].autoSort) { - this._buffers[buf]._sort(); - } - this._buffers[buf]._createIndex(); - if (jsonObject[this._dataSetName] - && jsonObject[this._dataSetName]["prods:hasChanges"]) { - this._buffers[buf]._loadBeforeImageData(jsonObject); - } - } - // Load nested data into _data - if (this._numBuffers > 1) { - for (var buf in this._buffers) { - if (this._buffers[buf]._isNested - && this._buffers[buf]._parent - && this._buffers[this._buffers[buf]._parent]) { - var srcData = this._buffers[this._buffers[buf]._parent]._data; - var data = []; - for (var i = 0; i < srcData.length; i++) { - if (srcData[i][buf] !== undefined) { - for (var j = 0; j < srcData[i][buf].length; j++) { - data.push(srcData[i][buf][j]); - } - delete srcData[i][buf]; - } - } - this._buffers[buf]._data = data; - if (this._buffers[buf].autoSort) { - this._buffers[buf]._sort(); - } - this._buffers[buf]._createIndex(); - } - } - } - } - } - else { - if (jsonObject instanceof Array) { - this._defaultTableRef._data = jsonObject; - } - else { - if (this._dataProperty) - this._defaultTableRef._data = jsonObject[this._dataProperty]; - else if (jsonObject.data) - this._defaultTableRef._data = jsonObject.data; - else { - this._defaultTableRef._data = []; - this._defaultTableRef._data[0] = jsonObject; - } - } - } - - for (var buf in this._buffers) { - if (this._buffers[buf].autoSort) { - this._buffers[buf]._sort(); - } - this._buffers[buf]._createIndex(); - } - }; - - /** - * Replace existing record data and index entry with new record data. - */ - this._mergeUpdateRecord = function (tableRef, recordId, record) { - var index = tableRef._index[recordId].index; - record._id = recordId; - - if (!tableRef._data[index]) { - tableRef._data[index] = {}; - } - this._copyRecord(this._tableRef, record, tableRef._data[index]); - record = tableRef._data[index]; - - if (tableRef._jsdo._resource.idProperty !== undefined) { - var id = tableRef._data[index][tableRef._jsdo._resource.idProperty]; - if (id !== undefined) { - delete tableRef._index[recordId]; - id += ""; - tableRef._index[id] = new progress.data.JSIndexEntry(index); - record._id = id; - } - } - - return record; - }; - - - /** - *update existing record data with specified error string - */ - this._setErrorString = function (tableRef, recordId, errorString, setInBeforeTable) { - - if (setInBeforeTable) { - // Ensure that object exists, it's null for deleted rows - if (tableRef._beforeImage[recordId]) { - tableRef._beforeImage[recordId]._errorString = errorString; - } - } - else { - var index = tableRef._index[recordId].index; - tableRef._data[index]._errorString = errorString; - } - }; - - /* - * Returns the array with the data from the specified dataObject. - */ - this._arrayFromDataObject = function (dataObject, tableRef) { - var data; - - if (dataObject === undefined) return undefined; - if (this._dataSetName) { - if (dataObject[this._dataSetName]) - data = dataObject[this._dataSetName][tableRef._name]; - } - else { - // check if data returned as array - if (dataObject instanceof Array) { - data = dataObject; - } else { - // or if data property is set - if (this._dataProperty) { - data = dataObject[this._dataProperty]; - } else if (dataObject.data) { - // or just try with 'data' as the data property name - data = dataObject.data; - } - } - } - - return data; - }; - - ///////////////////////////////////////////////////////////////////////////////////////////// - // - // Private method to merge changes after a create or update operation. - // This method is called to merge changes when server's Create or Update methods were called. - // - // It returns true if it found error for row in before-image data (prods:hasErrors = true) - // It returns false if there is no before-image data or prods:hasErrors property is absent - this._mergeUpdateForCUD = function (jsonObject, xhr) { - var hasError = false, - errorString; - - // Update dataset with changes from server - if (this._dataSetName) { - var dataSetJsonObject = jsonObject[this._dataSetName]; - - // only updates the specified record - var tableRef = xhr.request.jsrecord._tableRef; - var tableJsonObject = this._arrayFromDataObject(jsonObject, tableRef); - - if (tableJsonObject instanceof Array) { - if (tableJsonObject.length > 1) { - xhr.request.success = false; - throw new Error(msg.getMsgText("jsdoMSG100")); - } - - for (var i = 0; i < tableJsonObject.length; i++) { - var recordId = xhr.request.jsrecord.getId(); - - if (!recordId) { - throw new Error(msg.getMsgText("jsdoMSG034", "_mergeUpdateForCUD()")); - } - - // Determine if error string (get prods_id before _mergeUpdateRecord() is called, - // since it removes all prods properties) - errorString = undefined; - - if (tableJsonObject[i]["prods:hasErrors"]) { - var prods_id = tableJsonObject[i]["prods:id"]; - errorString = - this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); - hasError = true; - } - - var record = this._mergeUpdateRecord(tableRef, recordId, tableJsonObject[i]); - if (errorString) - this._setErrorString(tableRef, recordId, errorString, false); - - // Set _rejected property - if (tableJsonObject[i]["prods:rejected"] - || errorString) { - record._rejected = true; - if (errorString === "REJECTED") { - delete record._errorString; - } - } - - xhr.request.jsrecord = new progress.data.JSRecord(tableRef, record); - } - } - } else { - // update single record with changes from server - var tableRef = this._defaultTableRef; - var data = this._arrayFromDataObject(jsonObject); - - if (data instanceof Array) { - if (data.length > 1) { - xhr.request.success = false; - throw new Error(msg.getMsgText("jsdoMSG100")); - } - - for (var i = 0; i < data.length; i++) { - var recordId = xhr.request.jsrecord.getId(); - - if (!recordId) { - throw new Error(msg.getMsgText("jsdoMSG034", "_mergeUpdateForCUD()")); - } - - var record = this._mergeUpdateRecord(tableRef, recordId, data[i]); - xhr.request.jsrecord = new progress.data.JSRecord(tableRef, record); - } - } - } - - return hasError; - }; - - - ///////////////////////////////////////////////////////////////////////////////////////////// - // - // Private method to determine if deleted row (from delete operation) returned from AppServer - // was returned with an error in the before-image data. - // - // It returns true if it found an error for row in before-image data (prods:hasErrors = true) - // It returns false if there is no before-image data or prods:hasErrors property is absent - - this._checkForDeleteError = function (dataSetJsonObject, xhr) { - var hasError = false; - var tableRef = xhr.request.jsrecord._tableRef; - - beforeJsonObject = dataSetJsonObject["prods:before"]; - - // No merge is necessary for deletes, but we need to see - // if there are any errors on deletes records. - // delete records are not in after table, only in before table - if (beforeJsonObject) { - var beforeTableJsonObject = beforeJsonObject[tableRef._name]; - - if (beforeTableJsonObject.length > 1) { - xhr.request.success = false; - throw new Error(msg.getMsgText("jsdoMSG100")); - } - // clientId is same as _id - var recordId = beforeTableJsonObject[0]["prods:clientId"]; - if (!recordId) { - throw new Error(msg.getMsgText("jsdoMSG035", "_checkForDeleteError()")); - } - - // Determine if row was returned with error string - if (beforeTableJsonObject[0]["prods:hasErrors"]) { - var prods_id = beforeTableJsonObject[0]["prods:id"]; - var errorString = - this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); - this._setErrorString(tableRef, recordId, errorString, true); - hasError = true; - } - } - - return hasError; - }; - - ///////////////////////////////////////////////////////////////////////////////////////////// - // - // Private method to merge changes after a call to saveChanges. - // This method is called when saveChanges(useSubmit) was called with useSubmit=true. - // This can process/merge one or more created, deleted or updated records. - // In order for a jsonObject to have before-image data, it must be associated with a dataset. - // - // It only merges changes in the after table. But we need to look at before-image table to see - // if there were any errors passed back for the deletes - // - this._mergeUpdateForSubmit = function (jsonObject, xhr) { - var errorString; - - //if (!this._dataSetName || !jsonObject[this._dataSetName]["prods:hasChanges"]) - if (!this._dataSetName) { - // "_mergeUpdateForSubmit() can only be called for a dataset" - throw new Error(msg.getMsgText("jsdoMSG036", "_mergeUpdateForSubmit()")); - } - - // response is sent back with extra dataset object wrapper - var dataSetJsonObject = jsonObject[this._dataSetName]; - if (dataSetJsonObject[this._dataSetName]) - dataSetJsonObject = dataSetJsonObject[this._dataSetName]; - - var beforeJsonObject = dataSetJsonObject["prods:before"]; - - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - - var tableJsonObject = dataSetJsonObject[tableRef._name]; - if (tableJsonObject instanceof Array) { - for (var i = 0; i < tableJsonObject.length; i++) { - - var recordId = tableJsonObject[i]["prods:clientId"]; - if (!recordId) { - throw new Error(msg.getMsgText("jsdoMSG035", "_mergeUpdateForSubmit()")); - } - - // Determine if error string (get prods_id before _mergeUpdateRecord() is called, - // since it removes all prods properties) - errorString = undefined; - - if (tableJsonObject[i]["prods:hasErrors"]) { - var prods_id = tableJsonObject[i]["prods:id"]; - errorString = - this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); - } - var record = this._mergeUpdateRecord(tableRef, recordId, tableJsonObject[i]); - if (errorString) { - this._setErrorString(tableRef, recordId, errorString, false); - } - - // Set _rejected property so it can be checked in applyChanges() - if (tableJsonObject[i]["prods:rejected"] - || errorString) { - record._rejected = true; - if (errorString === "REJECTED") { - delete record._errorString; - } - } - - // Now need to update jsrecords. - // We use this data when we fire create, update and delete events. - // Updating so that it contains latest data (data sent back from server) - var jsrecords = xhr.request.jsrecords; - for (var idx = 0; idx < jsrecords.length; idx++) { - if (jsrecords[idx].data["_id"] == recordId) { - jsrecords[idx].data = record; - break; - } - } - } - } - } - - // No merge is necessary for deletes, - // but we need to see if there are any errors on deletes records. - // delete records are not in after table, only in before table - if (beforeJsonObject) { - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - var beforeTableJsonObject = beforeJsonObject[tableRef._name]; - var errorString; - - if (beforeTableJsonObject instanceof Array) { - for (var i = 0; i < beforeTableJsonObject.length; i++) { - - if (beforeTableJsonObject[i]["prods:rowState"] == "deleted") { - var recordId = beforeTableJsonObject[i]["prods:clientId"]; - if (!recordId) { - throw new Error(msg.getMsgText("jsdoMSG035", "_mergeUpdateForSubmit()")); - } - - errorString = undefined; - // If row was returned with error string, just copy that over to jsdo record - if (beforeTableJsonObject[i]["prods:hasErrors"]) { - var prods_id = beforeTableJsonObject[i]["prods:id"]; - - errorString = this._getErrorStringFromJsonObject(dataSetJsonObject, - tableRef, prods_id); - this._setErrorString(tableRef, recordId, errorString, true); - } - - // Set _rejected property so it can be checked in applyChanges() - if ((beforeTableJsonObject[i]["prods:rejected"] - || errorString) - && tableRef._beforeImage[recordId]) { - tableRef._beforeImage[recordId]._rejected = true; - if (errorString === "REJECTED") { - delete tableRef._beforeImage[recordId]._errorString; - } - } - } - } - } - } - } - }; - - ///////////////////////////////////////////////////////////////////////////////////////////// - // - // Private method that fires afterCreate, afterUpdate and afterDelete (CUD) triggers after - // saveChanges(true) is called. We must fire create, update and delete triggers - // for each record that was sent to backend submit operation - this._fireCUDTriggersForSubmit = function (request) { - // Before firing triggers, delete prods properties (except rowState) so they don't appear in data - for (var idx = 0; idx < request.jsrecords.length; idx++) { - this._deleteProdsProperties(request.jsrecords[idx].data, false, false); - } - - for (var idx = 0; idx < request.jsrecords.length; idx++) { - var jsrecord = request.jsrecords[idx]; - switch (jsrecord.data["prods:rowState"]) { - case "created": - jsrecord._tableRef.trigger("afterCreate", this, jsrecord, request.success, request); - this.trigger("afterCreate", this, jsrecord, request.success, request); - break; - case "modified": - jsrecord._tableRef.trigger("afterUpdate", this, jsrecord, request.success, request); - this.trigger("afterUpdate", this, jsrecord, request.success, request); - break; - case "deleted": - jsrecord._tableRef.trigger("afterDelete", this, jsrecord, request.success, request); - this.trigger("afterDelete", this, jsrecord, request.success, request); - break; - } - } - }; - - ////////////////////////////////////////////////////////////////////////////////////////////// - // - // Private method to return error for specified row - // from jsonObject's prods:errors object (before-data) sent over from AppServer - // - this._getErrorStringFromJsonObject = function (dataSetJsonObject, tableRef, prods_id) { - var tableJsonObject; - var errorsJsonObject = dataSetJsonObject["prods:errors"]; - - if (errorsJsonObject) { - tableJsonObject = errorsJsonObject[tableRef._name]; - } - - if (tableJsonObject instanceof Array) { - for (var i = 0; i < tableJsonObject.length; i++) { - - var id = tableJsonObject[i]["prods:id"]; - if (id === prods_id) { - var errorString = tableJsonObject[i]["prods:error"]; - return errorString === null ? - "Server returned unspecified error. Please check log files." : errorString; - } - } - } - - return undefined; - }; - - this._fillSuccess = function (jsdo, success, request) { - var xhr = request.xhr, - properties; - - // Need to check if responseMapping was specified; developer can specify - // plug-in to manipulate response - properties = jsdo.getMethodProperties("read"); - - if (properties && properties.mappingType) { - mapping = progress.data.PluginManager.getPlugin(properties.mappingType); - if (!mapping) { - throw new Error(progress.data._getMsgText("jsdoMSG118", properties.mappingType)); - } - - if (typeof (mapping.responseMapping) === "function") { - request.response = mapping.responseMapping(jsdo, request.response, { operation: "read" }); - } - } - - jsdo._clearData(); - jsdo._mergeRead(request.response, xhr); - - // Set working record - for (var buf in jsdo._buffers) { - if (!jsdo._buffers[buf]._parent || !jsdo.useRelationships) { - jsdo._buffers[buf]._setRecord(jsdo._buffers[buf]._findFirst()); - } - } - }; - - this._fillComplete = function (jsdo, success, request) { - jsdo.trigger("afterFill", jsdo, request.success, request); - if (request.deferred) { - if (success) { - request.deferred.resolve(jsdo, success, request); - } - else { - request.deferred.reject(jsdo, success, request); - } - } - }; - - this._fillError = function (jsdo, success, request) { - jsdo._clearData(); - jsdo._updateLastErrors(jsdo, null, null, request); - }; - - this._undoCreate = function (tableRef, id) { - // Undo operation - // Remove record from JSDO memory - var entry = tableRef._index[id]; - if (entry !== undefined) { - var index = entry.index; - tableRef._data[index] = null; - } - tableRef._hasEmptyBlocks = true; - delete tableRef._index[id]; - delete tableRef._beforeImage[id]; - // End - Undo operation - }; - - this._undoUpdate = function (tableRef, id, deleteProdsProps) { - /* Default to false */ - if (typeof(deleteProdsProps) == 'undefined') { - deleteProdsProps = false; - } - - // Undo operation - // Restore from before image - var record = tableRef._beforeImage[id]; - - // Before image points to an existing record - if (record) { - var index = tableRef._index[id].index; - tableRef._jsdo._copyRecord(tableRef, record, tableRef._data[index]); - if (deleteProdsProps) - tableRef._jsdo._deleteProdsProperties(tableRef._data[index], true); - } - delete tableRef._beforeImage[id]; - // End - Restore before image - }; - - this._undoDelete = function (tableRef, id, deleteProdsProps) { - /* Default to false */ - if (typeof(deleteProdsProps) == 'undefined') { - deleteProdsProps = false; - } - - // Restore from before image - var record = tableRef._beforeImage[id]; - - // Before image points to an existing record - if (record) { - var index = record._index; - delete record._index; - if (deleteProdsProps) - tableRef._jsdo._deleteProdsProperties(record, true); - - if ((index !== undefined) && (tableRef._data[index] === null)) { - tableRef._data[index] = record; - } - else { - tableRef._data.push(record); - index = tableRef._data.length - 1; - } - tableRef._index[id] = new progress.data.JSIndexEntry(index); - } - delete tableRef._beforeImage[id]; - // End - Restore before image - }; - - this._deleteComplete = function (jsdo, success, request) { - var xhr = request.xhr; - var jsrecord = request.jsrecord; - - try { - // Before firing trigger, delete prods properties so they don't appear in data - jsdo._deleteProdsProperties(jsrecord.data, false); - - jsrecord._tableRef.trigger("afterDelete", jsdo, jsrecord, request.success, request); - jsdo.trigger("afterDelete", jsdo, jsrecord, request.success, request); - - } finally { - request.complete = true; - jsdo._checkSaveComplete(xhr); - } - }; - - this._deleteSuccess = function (jsdo, success, request) { - var xhr = request.xhr; - var jsonObject = request.response; - var beforeJsonObject = null; - var dataSetJsonObject = null; - var data; - - //Even though this is _deleteSuccess, if before-image data is returned, the call of - // delete operation could return a success, but we have to check if error was returned - // in before-image data - var hasError = false; - if (jsdo._useBeforeImage("delete")) { - dataSetJsonObject = jsonObject[jsdo._dataSetName]; - beforeJsonObject = dataSetJsonObject["prods:before"]; - - if (beforeJsonObject) { - data = beforeJsonObject[request.jsrecord._tableRef._name]; - } - } - else { - data = jsdo._arrayFromDataObject(jsonObject, request.jsrecord._tableRef); - } - - if (data instanceof Array) { - if (data.length > 1) { - request.success = false; - throw new Error(msg.getMsgText("jsdoMSG100")); - } - } - - if (beforeJsonObject) { - hasError = jsdo._checkForDeleteError(dataSetJsonObject, xhr); - } - - if (hasError) - request.success = false; - - if (jsdo.autoApplyChanges) { - if (!hasError) { - // Clear before image - delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; - // End - Clear before image - } - else { - jsdo._deleteError(jsdo, success, request); - } - } - }; - - this._deleteError = function (jsdo, success, request) { - if (jsdo.autoApplyChanges) { - jsdo._undoDelete(request.jsrecord._tableRef, request.jsrecord.data._id); - } - }; - - this._createComplete = function (jsdo, success, request) { - var xhr = request.xhr; - var jsrecord = request.jsrecord; - - try { - // Before firing trigger, delete prods properties so they don't appear in data - jsdo._deleteProdsProperties(jsrecord.data, false); - - jsrecord._tableRef.trigger("afterCreate", jsdo, jsrecord, request.success, request); - jsdo.trigger("afterCreate", jsdo, jsrecord, request.success, request); - } finally { - request.complete = true; - jsdo._checkSaveComplete(xhr); - } - }; - - this._createSuccess = function (jsdo, success, request) { - var xhr = request.xhr; - var record = request.response; - var hasError = jsdo._mergeUpdateForCUD(record, xhr); - - if (hasError) - request.success = false; - - if (jsdo.autoApplyChanges) { - if (!hasError) { - // Clear before image - delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; - // End - Clear before image - } - else { - jsdo._createError(jsdo, success, request); - } - } - }; - - this._createError = function (jsdo, success, request) { - if (jsdo.autoApplyChanges) { - jsdo._undoCreate(request.jsrecord._tableRef, request.jsrecord.data._id); - } - }; - - - this._updateComplete = function (jsdo, success, request) { - var xhr = request.xhr; - var jsrecord = request.jsrecord; - try { - // Before firing trigger, delete prods properties so they don't appear in data - jsdo._deleteProdsProperties(jsrecord.data, false); - - jsrecord._tableRef.trigger("afterUpdate", jsdo, jsrecord, request.success, request); - jsdo.trigger("afterUpdate", jsdo, jsrecord, request.success, request); - } finally { - request.complete = true; - jsdo._checkSaveComplete(xhr); - } - }; - - this._updateSuccess = function (jsdo, success, request) { - var xhr = request.xhr; - var hasError = jsdo._mergeUpdateForCUD(request.response, xhr); - - if (hasError) { - request.success = false; - } - - if (jsdo.autoApplyChanges) { - if (!hasError) { - request.success = true; - // Clear before image - delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; - // End - Clear before image - } - else { - jsdo._updateError(jsdo, success, request); - } - } - }; - - this._updateError = function (jsdo, success, request) { - - if (jsdo.autoApplyChanges) { - request.success = false; - jsdo._undoUpdate(request.jsrecord._tableRef, request.jsrecord.data._id); - } - }; - - - this._saveChangesSuccess = function (jsdo, success, request) { - var records = request.response; - jsdo._mergeUpdateForSubmit(records, request.xhr); - - // Ensure that that the _lastErrors variable has been cleared - jsdo._clearErrors(); - var changes = jsdo.getChanges(); - jsdo._updateLastErrors(jsdo, null, changes); - - jsdo._setAllRecordsRejected(changes); - - if (jsdo.autoApplyChanges) { - jsdo._applyChanges(); - } - }; - - - this._saveChangesError = function (jsdo, success, request) { - jsdo._setAllRecordsRejected(true); - if (jsdo.autoApplyChanges) { - jsdo.rejectChanges(); - } - jsdo._updateLastErrors(jsdo, null, null, request); - }; - - /* _saveChangesSuccessTT - internal function called after a Submit of a temp-table (not DataSet) returns success - This method does not attempt to do any merging of records into the JSDO memory. The - absence of _id for the records means that the only way we could possibly do a "merge" - would be to delete the changed rceords in the JSDO memory and then add the records - that were returned form the data service, but that would invalidate the _id's that - the Kendo datasource depends on. The application programmmer must do the merging in - the afterSaveChanges handler - - *** Submit(temp-table) is not supported. This method will be removed in a future version. *** - */ - this._saveChangesSuccessTT = function (jsdo, success, request) { - var changes; - - // Ensure that that the _lastErrors variable has been cleared - jsdo._clearErrors(); - changes = jsdo.getChanges(); - jsdo._updateLastErrors(jsdo, null, changes); - jsdo._setAllRecordsRejected(false); - }; - - this._saveChangesComplete = function (jsdo, success, request) { - // Success with errors - if ((request.xhr.status >= 200 && request.xhr.status < 300) - && (jsdo._lastErrors.length > 0 || jsdo._someRecordsRejected)) { - request.success = false; - } - - // If saveChanges(true) was called, then we must fire create, update and delete triggers - // for each record that was sent to submit operation - if (jsdo._useSubmit === true) { - jsdo._fireCUDTriggersForSubmit(request); - } - - jsdo._undefWorkingRecord(); - jsdo._fireAfterSaveChanges(request.success, request); - - }; - - this._fireAfterSaveChanges = function (success, request) { - this.trigger("afterSaveChanges", this, success, request); - - if (request.jsrecords) { - if (request.deferred) { - if (success) { - request.deferred.resolve(this, success, request); - } - else { - request.deferred.reject(this, success, request); - } - } - } - else if (request.batch && request.batch.deferred) { - if (success) { - request.batch.deferred.resolve(this, success, request); - } - else { - request.batch.deferred.reject(this, success, request); - } - } - - // Clear error string when autoApplyChanges is true - var clearErrorString = this.autoApplyChanges; - - // This will be set if submit operation was performed - if (request.jsrecords) { - for (var idx = 0; idx < request.jsrecords.length; idx++) { - var jsrecord = request.jsrecords[idx]; - if (clearErrorString) { - delete jsrecord.data._errorString; - } - delete jsrecord.data["prods:rowState"]; - } - } - else if (request.batch && request.batch.operations) { - for (var idx = 0; idx < request.batch.operations.length; idx++) { - var jsrecord = request.batch.operations[idx].jsrecord; - if (clearErrorString) { - delete jsrecord.data._errorString; - } - } - } - }; - - /* - * Returns errors in response associated with the HTTP request.records related to the specified jsrecord. - */ - this._getErrorsFromRequest = function(request) { - var errors = [], // Array of objects with properties: type, id, error, errorNum, responseText - errorArray = [], - errorObject, - retValString, - j, - i; - - if (request && !request.success) { - if (request.xhr.status >= 400 && request.xhr.status < 600) { - try { - responseObject = JSON.parse(request.xhr.responseText); - - // responseText could be an array, an object or just text. - // If it is an array, each object would have properties _errors and optional _retVal. - // If it is not an array, the object would have properties _errors and optional _retVal. - // If it is text, the content could also be an HTML page, this error is handle using "HTTP Status". - if (responseObject instanceof Array) { - errorArray = responseObject; - } else if (responseObject instanceof Object) { - errorArray.push(responseObject); - } - for (i = 0; i < errorArray.length; i += 1) { - errorObject = errorArray[i]; - if (errorObject._retVal) { - errors.push({ - type: progress.data.JSDO.RETVAL, - error: errorObject._retVal - }); - retValString = errorObject._retVal; - } else { - retValString = null; - } - if (errorObject._errors instanceof Array) { - for (j = 0; j < errorObject._errors.length; j += 1) { - if ((errorObject._errors[j]._errorNum === 0) - && (errorObject._errors[j]._errorMsg === retValString)) { - // Suppress additional error msg if it is same as return value - continue; - } - errors.push({ - type: progress.data.JSDO.APP_ERROR, - error: errorObject._errors[j]._errorMsg, - errorNum: errorObject._errors[j]._errorNum - }); - } - } - } - } - catch (e) { - // Ignore exceptions - } - } - if (request.exception) { - errors.push({ - type: progress.data.JSDO.ERROR, - error: request.exception - }); - } - if (errors.length === 0 - && request.xhr - && (request.xhr.status >= 400 && request.xhr.status < 600)) { - errors.push({ - type: progress.data.JSDO.ERROR, - error: "Error: HTTP Status " + request.xhr.status + " " + request.xhr.statusText, - responseText: request.xhr.responseText - }); - } - } - return errors; - }; - - this._updateLastErrors = function (jsdo, batch, changes, request) { - var errors, - errorText, - responseObject, - i, - j, - buf; - - if (batch) { - if (batch.operations === undefined) return; - for (i = 0; i < batch.operations.length; i++) { - request = batch.operations[i]; - if (!request.success && request.xhr) { - if (request.xhr.status >= 200 && request.xhr.status < 300) { - // Add error string to jsdo._lastErrors - jsdo._lastErrors.push({errorString: request.jsrecord.data._errorString}); - // Add error object to jsdo.._lastErrors - jsdo._buffers[request.jsrecord._tableRef._name]._lastErrors.push({ - type: progress.data.JSDO.DATA_ERROR, - id: request.jsrecord.data._id, - error: request.jsrecord.data._errorString}); - } - else { - errors = this._getErrorsFromRequest(request); - errorText = ""; - for (j = 0; j < errors.length; j += 1) { - if (errors.length > 1 && errors[j].error.indexOf("(7243)") != -1) { - // If there are more error messages - // supress error "The Server application has returned an error. (7243)" - continue; - } - // Add error to table reference - if (request.jsrecord - && (errors[j].type === progress.data.JSDO.APP_ERROR - || errors[j].type === progress.data.JSDO.RETVAL)) { - errors[j].id = request.jsrecord.data._id; - request.jsrecord._tableRef._lastErrors.push(errors[j]); - } - if (errorText.length === 0) { - errorText = errors[j].error; - } - else { - errorText += "\n" + errors[j].error; - } - } - // Add error string to jsdo._lastErrors - jsdo._lastErrors.push({errorString: errorText}); - } - } - } - } - else if (changes instanceof Array) { - for (i = 0; i < changes.length; i++) { - if (changes[i].record && changes[i].record.data._errorString !== undefined) { - jsdo._lastErrors.push({errorString: changes[i].record.data._errorString}); - jsdo._buffers[changes[i].record._tableRef._name]._lastErrors.push({ - type: progress.data.JSDO.DATA_ERROR, - id: changes[i].record.data._id, - error: changes[i].record.data._errorString}); - } - } - } - else if (request - && !request.success - && request.xhr - && ((request.xhr.status >= 400 && request.xhr.status < 600) || request.xhr.status === 0)) { - errors = this._getErrorsFromRequest(request); - errorText = ""; - for (j = 0; j < errors.length; j += 1) { - if (errors.length > 1 && errors[j].error.indexOf("(7243)") != -1) { - // If there are more error messages - // supress error "The Server application has returned an error. (7243)" - continue; - } - // Add error to all table references - for (buf in this._buffers) { - this._buffers[buf]._lastErrors.push(errors[j]); - } - if (errorText.length === 0) { - errorText = errors[j].error; - } - else { - errorText += "\n" + errors[j].error; - } - } - jsdo._lastErrors.push({errorString: errorText}); - } - }; - - // Check if all the xhr operations associated with the batch for which - // this xhr object is related have completed (not necessarily to success). - // If all XHR operations have completed this fires 'afterSaveChanges' event - this._checkSaveComplete = function (xhr) { - if (xhr.request) { - var jsdo = xhr.request.jsdo; - var batch = xhr.request.batch; - // OE00229270 Should only do afterSaveChanges if _async - if (jsdo && batch && jsdo._async) { - if (jsdo._isBatchComplete(batch)) { - var success = jsdo._isBatchSuccess(batch); - var request = { - batch: batch, - success: success - }; - jsdo._undefWorkingRecord(); - - // Save error messages - jsdo._lastErrors = []; - if (!success && batch.operations) { - jsdo._updateLastErrors(jsdo, batch, null); - } - this._setAllRecordsRejected(batch); - - jsdo._fireAfterSaveChanges(success, request); - } - } - } - }; - - - /* - * determine if a batch of XHR requests has completed in which all requests are successful - */ - this._isBatchSuccess = function (batch) { - if (batch.operations) { - for (var i = 0; i < batch.operations.length; i++) { - if (!batch.operations[i].success) { - return false; - } - } - } - return true; - }; - - /* - * determine if all XHR requests from the batch of saves have completed (not necessarily to success) - */ - this._isBatchComplete = function (batch) { - if (batch.operations) { - for (var i = 0; i < batch.operations.length; i++) { - var request = batch.operations[i]; - // we have to check against the 'complete' flag because xhr.readyState - // might be set async by the browser - // while we're still in the middle of processing some other requests's response - if (!request.complete) { - return false; - } - } - } - return true; - }; - - this._mergeInvoke = function (jsonObject, xhr) { - var operation; - if (xhr.request.fnName !== undefined - && xhr.jsdo._resource.fn[xhr.request.fnName] !== undefined) { - operation = xhr.jsdo._resource.fn[xhr.request.fnName].operation; - } - else - operation = null; - if (operation === undefined) { - // Operation data is only required for invoke operations with mergeMode: true - operation = null; - for (var i = 0; i < xhr.jsdo._resource.operations.length; i++) { - if (xhr.jsdo._resource.operations[i].name == xhr.request.fnName) { - operation = xhr.jsdo._resource.operations[i]; - break; - } - } - xhr.jsdo._resource.fn[xhr.request.fnName].operation = operation; - } - if (operation !== null && operation.mergeMode) { - try { - var mergeMode = progress.data.JSDO["MODE_" + operation.mergeMode.toUpperCase()]; - if (mergeMode === null) { - throw new Error(msg.getMsgText("jsdoMSG030", "mergeMode property", - "EMPTY, APPEND, MERGE or REPLACE")); - } - if (xhr.jsdo._resource.idProperty === undefined) { - throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, - " by mergeMode property in invoke operation")); - } - var dataParameterName; - if (xhr.jsdo.isDataSet()) { - dataParameterName = xhr.jsdo._resource._dataSetName; - } - else if (xhr.jsdo._resource.dataProperty !== undefined) { - dataParameterName = xhr.jsdo._resource.dataProperty; - } - else if (xhr.jsdo._resource._tempTableName !== undefined) { - dataParameterName = xhr.jsdo._resource._tempTableName; - } - else { - throw new Error(msg.getMsgText("jsdoMSG111", "")); - } - - var found = false; - for (var i = 0; i < operation.params.length; i++) { - if (operation.params[i].name == dataParameterName) { - if (operation.params[i].type.indexOf('RESPONSE_BODY') != -1) { - if ((operation.params[i].xType !== undefined) - && (operation.params[i].xType != 'DATASET') - && (operation.params[i].xType != 'TABLE') - && (operation.params[i].xType != 'ARRAY')) { - throw new Error(msg.getMsgText("jsdoMSG113", operation.params[i].xType, - dataParameterName, xhr.request.fnName)); - } - found = true; - break; - } - } - } - - if (!found) { - throw new Error(msg.getMsgText("jsdoMSG112", dataParameterName, xhr.request.fnName)); - } - xhr.jsdo.addRecords(xhr.request.response[dataParameterName], - mergeMode, [xhr.jsdo._resource.idProperty], false, true); - } - catch (e) { - xhr.request.success = false; - xhr.request.exception = e; - } - } - }; - - this.onReadyStateChangeGeneric = function () { - var xhr = this; - if (xhr.readyState == 4) { - var request = xhr.request; - - /* try to parse response even if request is considered "failed" due to http status */ - try { - request.response = JSON.parse(xhr.responseText); - // in some cases the object back from appserver has a "response" property which represents - // the real content of the JSON...happens when multiple output parameters are returned. - // this of course assumes no one names their root object "response". - if (request.response && request.response.response) { - request.response = request.response.response; - } - } catch (e) { - request.response = undefined; - } - - try { - if ((xhr.status >= 200 && xhr.status < 300) - || (xhr.status === 0 && xhr.responseText !== "")) { - - request.success = true; - // get the Client Context ID (AppServer ID) - xhr.jsdo._session._saveClientContextId(xhr); - if ((typeof xhr.onSuccessFn) == 'function') { - var operation; - if (xhr.request.fnName !== undefined - && xhr.jsdo._resource.fn[xhr.request.fnName] !== undefined) { - operation = xhr.jsdo._resource.fn[xhr.request.fnName].operation; - } - else - operation = null; - if ((operation === undefined) || (operation !== null && operation.mergeMode)) - xhr.jsdo._mergeInvoke(request.response, xhr); - if (request.success) - xhr.onSuccessFn(xhr.jsdo, request.success, request); - else if ((typeof xhr.onErrorFn) == 'function') - xhr.onErrorFn(xhr.jsdo, request.success, request); - } - - } else { - request.success = false; - if (xhr.status === 0) { - request.exception = new Error(msg.getMsgText("jsdoMSG101")); - } - if ((typeof xhr.onErrorFn) == 'function') { - xhr.onErrorFn(xhr.jsdo, request.success, request); - } - } - } catch (e) { - request.success = false; - request.exception = e; - if ((typeof xhr.onErrorFn) == 'function') { - xhr.onErrorFn(xhr.jsdo, request.success, request); - } - } - // get the Client Context ID (AppServer ID) - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - - if ((typeof xhr.onCompleteFn) == 'function') { - xhr.onCompleteFn(xhr.jsdo, request.success, request); - } - - } - }; - - /* - * Accepts changes for all table references in the JSDO. - */ - this.acceptChanges = function () { - for (var buf in this._buffers) { - this._buffers[this._buffers[buf]._name].acceptChanges(); - } - }; - - /* - * Rejects changes for the table references in the JSDO. - */ - this.rejectChanges = function () { - for (var buf in this._buffers) { - this._buffers[this._buffers[buf]._name].rejectChanges(); - } - }; - - /* - * Returns an array with changes for all table references in the JSDO. - */ - this.getChanges = function () { - var result = []; - for (var buf in this._buffers) { - var changes = this._buffers[this._buffers[buf]._name].getChanges(); - result = result.concat(changes); - } - return result; - }; - - this.hasChanges = function () { - for (var buf in this._buffers) { - if (this._buffers[this._buffers[buf]._name].hasChanges()) - return true; - } - return false; - }; - - /* - * Private method to apply changes for all table references in the JSDO. - * If _errorString has been set for a row, rejectRowChanges() is called. - * If it has not been set, acceptRowChanges() is called. - */ - this._applyChanges = function () { - for (var buf in this._buffers) { - this._buffers[this._buffers[buf]._name]._applyChanges(); - } - }; - - /* - * Accepts row changes for the working record using the JSDO reference. - */ - this.acceptRowChanges = function () { - if (this._defaultTableRef) - return this._defaultTableRef.acceptRowChanges(); - throw new Error(msg.getMsgText("jsdoMSG001", "acceptRowChanges()")); - }; - - /* - * Reject row changes for the working record using the JSDO reference. - */ - this.rejectRowChanges = function () { - if (this._defaultTableRef) - return this._defaultTableRef.rejectRowChanges(); - throw new Error(msg.getMsgText("jsdoMSG001", "rejectRowChanges()")); - }; - - /* - * Sets complete set of properties for the jsdo. All existing properties are replaced with new set - */ - this.setProperties = function( propertiesObject ) { - var prop; - - if (arguments.length < 1) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'setProperties', 1)); - } - if (arguments.length > 1) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'setProperties', 1)); - } - if ( typeof propertiesObject == "object" ) { - /* Copy properties of the propertiesObject argument into _properties. - * Note that if object passed in has a prototype, this code copies them too) - */ - this._properties = {}; - - for (prop in propertiesObject) { - if( propertiesObject.hasOwnProperty(prop) ) { - if (typeof propertiesObject[prop] !== "function" ) { - this._properties[prop] = propertiesObject[prop]; - } - } - } - } - else if ( (propertiesObject === undefined) || (propertiesObject === null) ) { - this._properties = {}; - } - else { - // {1}: Parameter {1} must be of type {3} in {4} call. - throw new Error(progress.data._getMsgText("jsdoMSG121", 'JSDO', 1, 'Object', - 'setProperties')); - } - }; - - /* - * Set or remove an individual property in the property set maintained by the jsdo. - * This operates only on the property identified by propertyName; - * all other existing properties remain as they are. - * If the propertyName is not part of the context, this call adds it. - * If it exists, it is updated, unless - - * If propertyValue is undefined, this call removes the property - */ - this.setProperty = function( propertyName, propertyValue) { - if (arguments.length < 2) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', - 'setProperty', 2)); - } - if (arguments.length !== 2) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", "JSDO", - "setProperty", 2)); - } - if (typeof propertyName !== "string") { - // {1}: Parameter {1} must be of type {3} in {4} call. - throw new Error(progress.data._getMsgText("jsdoMSG121", 'JSDO', 1, 'string', - 'setProperty')); - } - - if ( propertyValue === undefined ) { - delete this._properties[propertyName]; // OK if it doesn't exist -- no error - } - else { - this._properties[propertyName] = propertyValue; - } - }; - - /* - * Gets the set of jsdo properties. Returns an object containing all the properties - */ - this.getProperties = function( ) { - if (arguments.length > 0) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperties', 0)); - } - return this._properties; - }; - - /* Gets the value of an individual property in the jsdo property set - */ - this.getProperty = function( propertyName) { - if (arguments.length < 1) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperty', 1)); - } - if (arguments.length > 1) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperty', 1)); - } - return this._properties[propertyName]; - - }; - - /////////////////////////////////////////////////////////////////////////// - // - // The following methods provide support for Object Pesistence - - /* - * Saves JSDO memory (and optionally pending changes) to local storage. - * - * saveLocal() - * saveLocal(name) - * saveLocal(dataMode) - * saveLocal(name, dataMode) - * - */ - this.saveLocal = function saveLocal(arg1, arg2) { - var name; - var dataMode; - - if (arguments.length > 2) { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); - } - - if (typeof(arg1) == 'string' || arg1 === null || arg1 === undefined) { - name = arg1; - dataMode = arg2; - } - else { - name = null; - dataMode = arg1; - } - - if (name === undefined || name === null || name === "") { - name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; - } - if (typeof(dataMode) == 'undefined') { - dataMode = progress.data.JSDO.ALL_DATA; - } - else { - switch (dataMode) { - case progress.data.JSDO.ALL_DATA: - case progress.data.JSDO.CHANGES_ONLY: - break; - default: - throw new Error(msg.getMsgText("jsdoMSG115", arguments.callee.name)); - } - } - - if (this._localStorage === null) { - // Must first instantiate _localStorage object - this._localStorage = new progress.data.LocalStorage(); - } - - var dataObj = this._prepareDataObjectForLocalStorage(dataMode); - this._localStorage.saveToLocalStorage(name, dataObj); - }; - - /* - * Reads localStorage (based upon name) into JSDO memory - * (localStorage may or may not have pending changes). - * readLocal() - * readLocal(name) - * - */ - this.readLocal = function readLocal(name) { - if (arguments.length > 1) { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); - } - if (name === undefined || name === null || name === "") { - name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; - } - else if (typeof(name) != 'string') { - throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); - } - - if (this._localStorage === null) { - this._localStorage = new progress.data.LocalStorage(); - } - - var object = this._localStorage.readFromLocalStorage(name); - - // If storage area does not exist (i.e. object = null) then don't update JSDO local memory - if (object) { - if (this._hasMatchingSchema(object) === false) - throw new Error(msg.getMsgText("jsdoMSG117", name)); - - // For readLocal(), JSDO should first be emptied of data, so using MODE_EMPTY - this._restoreFromLocalStorage(object, progress.data.JSDO.MODE_EMPTY); - } - - return object !== null; - }; - - /* - * Reads localStorage (based upon name) into JSDO memory - * (localStorage may or may not have pending changes). - * addLocalRecords(addMode) - * addLocalRecords(addMode, keyFields) - * addLocalRecords(name, addMode) - * addLocalRecords(name, addMode, keyFields) - */ - this.addLocalRecords = function addLocalRecords(arg1, arg2, arg3) { - var name; - var addMode; - var keyFields; - - if (arguments.length < 1) { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); - } - - if (typeof(arg1) == 'string') { - name = arg1; - addMode = arg2; - keyFields = arg3; - } - else { - name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; - addMode = arg1; - keyFields = arg2; - } - - if (typeof(name) == 'undefined' || name === null || name === "") { - name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; - } - else if (typeof(name) != 'string') { - throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); - } - - if (addMode != progress.data.JSDO.MODE_REPLACE) { - throw new Error(msg.getMsgText("jsdoMSG115", arguments.callee.name)); - } - - if (this._localStorage === null) { - this._localStorage = new progress.data.LocalStorage(); - } - - var object = this._localStorage.readFromLocalStorage(name); - - // If storage area does not exist (i.e. object = null) then don't update JSDO local memory - if (object) { - if (this._hasMatchingSchema(object) === false) - throw new Error(msg.getMsgText("jsdoMSG117", name)); - - try { - this._restoreFromLocalStorage(object, addMode, keyFields); - } - catch (e) { - var text = e.message; - throw new Error(text.replace(new RegExp('addRecords', 'g'), 'addLocalRecords')); - } - } - - return object !== null; - }; - - - /* - * This method returns True if each buffer in the jsdo contains a primary key. - */ - this._containsPrimaryKeys = function _containsPrimaryKeys() { - - for (var buf in this._buffers) { - if (this._buffers[buf]._primaryKeys === null) - return false; - } - - return true; - }; - - /* - * Compares JSDO's dataset/table names with those in specified storage object. - * Returns true if they match (or if storageObject is null or empty), else false. - */ - this._hasMatchingSchema = function _hasMatchingSchema(storageObject) { - var isValid = true; - - if (storageObject === null || (Object.keys(storageObject).length === 0)) - return true; - - - if (this._dataSetName) { - if (storageObject[this._dataSetName]) { - for (var buf in this._buffers) - if (storageObject[this._dataSetName][buf] === undefined) { - isValid = false; - break; - } - } - else - isValid = false; // dataset should be in storage area - } - else if (this._dataProperty) { - // If array, we had to wrap in "fake" dataset, so unwrap it - storageObject = storageObject["_localStorage"]; - if (storageObject === undefined || storageObject[this._dataProperty] === undefined) - isValid = false; - } - else { - // If temp-table, we had to wrap in "fake" dataset, so unwrap it - storageObject = storageObject["_localStorage"]; - if (storageObject === undefined || storageObject[this._defaultTableRef._name] === undefined) - isValid = false; - } - - return isValid; - }; - - - /* - * Clears the data saved to local storage. - * - * deleteLocal() - * deleteLocal(name) - */ - this.deleteLocal = function deleteLocal(name) { - if (arguments.length > 1) { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); - } - if (name === undefined || name === null || name === "") { - name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; - } - else if (typeof(name) != 'string') { - throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); - } - - if (this._localStorage === null) { - this._localStorage = new progress.data.LocalStorage(); - } - this._localStorage.clearLocalStorage(name); - }; - - - // This method is used by saveLocal() to return a jsonObject with current JSDO data based upon option. - // - // In order to take advantage of existing code (createChangeSet() and addRecords()) and particularly - // to use the processing of before-data in addRecords(), for tables and arrays, we create a dummy - // dataset name: _localStorage. - this._prepareDataObjectForLocalStorage = function (option) { - - var storageObject = {}; - - // DataSets - if (this._dataSetName) { - switch (option) { - case progress.data.JSDO.ALL_DATA: - storageObject = this._createDataAndChangeSet(this._dataSetName); - break; - - case progress.data.JSDO.CHANGES_ONLY: - storageObject = this._createChangeSet(this._dataSetName, true); - break; - } - } - // Arrays - else if (this._dataProperty) { - switch (option) { - case progress.data.JSDO.ALL_DATA: - storageObject = this._createDataAndChangeSet("_localStorage"); - break; - - case progress.data.JSDO.CHANGES_ONLY: - storageObject = this._createChangeSet("_localStorage", true); - break; - } - } - // Temp Tables - else { - switch (option) { - case progress.data.JSDO.ALL_DATA: - storageObject = this._createDataAndChangeSet("_localStorage"); - break; - - case progress.data.JSDO.CHANGES_ONLY: - storageObject = this._createChangeSet("_localStorage", true); - break; - } - } - - return storageObject; - }; - - - // Restore the data retrieved from local storage to the JSDO based upon the specified addMode - this._restoreFromLocalStorage = function (storageObject, addMode, keyFields) { - - if (storageObject && (Object.keys(storageObject).length > 0)) { - if (this._dataSetName) { - // Walk thru all tables to retrieve data - for (var buf in this._buffers) - this._restoreDataForTable(this._buffers[buf], storageObject, addMode, keyFields); - } - // Either temp-table or array - else - this._restoreDataForTable(this._defaultTableRef, storageObject, addMode, keyFields); - } - else if (addMode === progress.data.JSDO.MODE_EMPTY) - this._clearData(); - }; - - - this._restoreDataForTable = function (tableRef, jsonObject, addMode, keyFields) { - - // If primaryKeys not found, check if the idProperty is there - keyFields = keyFields !== undefined ? keyFields : tableRef._primaryKeys; - if (keyFields === undefined && this._resource.idProperty) { - keyFields = []; - keyFields[0] = this._resource.idProperty; - } - - if (this._dataSetName) { - var oldUseRelationships = this.useRelationships; - // Turn off useRelationships since addRecords() does not use the working record - this.useRelationships = false; - - try { - tableRef.addRecords(jsonObject, addMode, keyFields); - } finally { - // Restore useRelationships - this.useRelationships = oldUseRelationships; - } - } - // else it's either an array (this._dataProperty) or a temp-table - else { - // Creating dummy dataset name: "_localStorage" for tables and arrays - this._dataSetName = "_localStorage"; - tableRef.addRecords(jsonObject, addMode, keyFields); - this._dataSetName = null; - } - }; - - this.getMethodProperties = function(operation, name) { - var idx; - - if (this._resource._operations) { - if (this._resource._operations[operation]) { - return this._resource._operations[operation]; - } - } - else { - this._resource._operations = {}; - } - for (var idx = 0; idx < this._resource.operations.length; idx++) { - if (this._resource.operations[idx].type == operation) { - return (this._resource._operations[operation] = this._resource.operations[idx]); - } - } - }; - - /////////////////////////////////////////////////////////////////////////// - - // Load data - if (autoFill) - this.fill(); - - }; // End of JSDO - - // Constants for progress.data.JSDO - if ((typeof Object.defineProperty) == 'function') { - Object.defineProperty(progress.data.JSDO, 'MODE_APPEND', { - value: 1, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'MODE_EMPTY', { - value: 2, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'MODE_MERGE', { - value: 3, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'MODE_REPLACE', { - value: 4, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'ERROR', { - value: -1, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'APP_ERROR', { - value: -2, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'RETVAL', { - value: -3, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'DATA_ERROR', { - value: -4, - enumerable: true - }); - } else { - progress.data.JSDO.MODE_APPEND = 1; - progress.data.JSDO.MODE_EMPTY = 2; - progress.data.JSDO.MODE_MERGE = 3; - progress.data.JSDO.MODE_REPLACE = 4; - } - - /* CRUD */ - progress.data.JSDO._OP_CREATE = 1; - progress.data.JSDO._OP_READ = 2; - progress.data.JSDO._OP_UPDATE = 3; - progress.data.JSDO._OP_DELETE = 4; - progress.data.JSDO._OP_SUBMIT = 5; - - /* Offline support: saving data to local storage */ - progress.data.JSDO.ALL_DATA = 1; - progress.data.JSDO.CHANGES_ONLY = 2; - - // Arrays elements as individual fields - // Separator must have at least one characters - progress.data.JSDO.ARRAY_INDEX_SEPARATOR = "_"; - -// setup inheritance for JSDO - progress.data.JSDO.prototype = new progress.util.Observable(); - progress.data.JSDO.prototype.constructor = progress.data.JSDO; - progress.data.JSDO.prototype.toString = function (radix) { - return "JSDO"; - }; - -// setup inheritance for table reference - progress.data.JSTableRef.prototype = new progress.util.Observable(); - progress.data.JSTableRef.prototype.constructor = progress.data.JSTableRef; - progress.data.JSTableRef.prototype.toString = function (radix) { - return "JSTableRef"; - }; - - // Built-in Plugins - progress.data.PluginManager.addPlugin("JFP", { - requestMapping: function(jsdo, params, info) { - var sortFields, - field, - fieldName, - fieldInfo, - tableName, - filter, - sortDir, - ablFilter, - sqlQuery, - methodProperties, - capabilities, - index, - position, - option, - capabilitiesObject, - reqCapabilities = { - filter: { options: [ "ablFilter", "sqlQuery" ], mapping: undefined }, - top: { options: [ "top" ], mapping: undefined }, - skip: { options: [ "skip" ], mapping: undefined }, - id: { options: [ "id" ], mapping: undefined }, - sort: { options: [ "orderBy" ], mapping: undefined } - }, - doConversion = true, - param; - - if (info.operation === "read") { - capabilitiesObject = {}; - methodProperties = jsdo.getMethodProperties(info.operation); - capabilities = methodProperties.capabilities; - - if (capabilities) { - capabilities = capabilities.replace(/\s/g, "").split(","); - for (index = 0; index < capabilities.length; index += 1) { - capabilitiesObject[capabilities[index]] = true; - } - } - for (param in params) { - if (param && (params[param] !== undefined) && reqCapabilities[param]) { - for (index = 0; index < reqCapabilities[param].options.length; index += 1) { - option = reqCapabilities[param].options[index]; - if (capabilitiesObject[option]) { - reqCapabilities[param].mapping = option; - break; - } - } - if (!reqCapabilities[param].mapping) { - throw new Error(msg.getMsgText("jsdoMSG120", - reqCapabilities[param].options.join("' or '"), param)); - } - } - } - - if (jsdo._defaultTableRef && params.tableRef === undefined) { - tableName = jsdo._defaultTableRef._name; - } - else { - tableName = params.tableRef; - } - - if (params.sort) { - // Convert sort expression to JFP format - - if (typeof(params.sort) === "object" && !(params.sort instanceof Array)) { - // Kendo UI sort format - object - // Make params.sort an array - params.sort = [params.sort]; - } - sortFields = ""; - for (index = 0; index < params.sort.length; index += 1) { - field = params.sort[index]; - sortDir = ""; - - if (typeof(field) === "string") { - // setSortFields format - // Extract fieldName and sortDir from string - fieldName = field; - position = field.indexOf(":"); - if (position !== -1) { - sortDir = fieldName.substring(position + 1); - fieldName = fieldName.substring(0, position); - switch(sortDir.toLowerCase()) { - case "desc": - case "descending": - sortDir = "desc"; - break; - } - } - } else { - // Kendo UI sort format - array - // Extract fieldName and sortDir from object - fieldName = field.field; - if (params.sort[index].dir === "desc") { - sortDir = params.sort[index].dir; - } - } - if (tableName) { - // Use original fieldName instead of serialized name - fieldInfo = jsdo[tableName]._fields[fieldName.toLowerCase()]; - if (fieldInfo && fieldInfo.origName) { - fieldName = fieldInfo.origName; - } - } - if (sortDir === "desc") { - fieldName += " DESC"; - } - sortFields += fieldName; - if (index < params.sort.length - 1) { - sortFields += ","; - } - } - } - - if (params.filter) { - // If filter is specified as string, then no conversion is necessary - if (typeof params.filter === 'string') { - doConversion = false; - } - - params.tableRef = tableName; - - if (doConversion && (params.tableRef === undefined)) { - throw new Error(msg.getMsgText("jsdoMSG045", "fill() or read()", "params", - "tableRef")); - } - - if (reqCapabilities["filter"].mapping === "ablFilter") { - if (doConversion) { - ablFilter = progress.util._convertToABLWhereString( - jsdo._buffers[params.tableRef], params.filter); - } - else { - ablFilter = params.filter; - } - } - else if (reqCapabilities["filter"].mapping === "sqlQuery") { - if (doConversion) { - sqlQuery = progress.util._convertToSQLQueryString( - jsdo._buffers[params.tableRef], params.filter, true); - } - else { - sqlQuery = params.filter; - } - } - } - - filter = JSON.stringify({ - ablFilter: ablFilter, - sqlQuery: sqlQuery, - orderBy: sortFields, - skip: params.skip, - top: params.top - }); - - params = {filter: filter}; - } - return params; - } - }); - - if (typeof progress.ui == 'undefined') - progress.ui = {}; - progress.ui.UITableRef = function UITableRef(tableRef) { - this._tableRef = tableRef; - this._listview = null; - this._detailPage = null; - this._listviewContent = undefined; - - this.addItem = function (format) { - var detailForm; - - if (!this._tableRef.record) - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - - if (!this._listview) return; - - format = format ? format : this._listview.format; - detailForm = (this._detailPage && this._detailPage.name) ? this._detailPage.name : ""; - - if (this._listviewContent === undefined) { - this.clearItems(); - } - var text = this._listview.itemTemplate ? - this._listview.itemTemplate : progress.ui.UIHelper._itemTemplate; - - text = text.replace(new RegExp('{__format__}', 'g'), format); - text = text.replace(new RegExp('{__id__}', 'g'), this._tableRef.record.data._id); - text = text.replace(new RegExp('{__page__}', 'g'), detailForm); - - for (var field in this._tableRef.record.data) { - var value = this._tableRef.record.data[field]; - text = text.replace(new RegExp('{' + field + '}', 'g'), - (value !== undefined && value !== null) ? value : ""); - } - - this._listviewContent += text; - }; - - this.clearItems = function () { - if (this._listview) { - this._listviewContent = ''; - var listviewElement = document.getElementById(this._listview.name); - if (listviewElement) { - listviewElement.innerHTML = ''; - } - } - }; - - this._getFormFieldValue = function (fieldName, detailPageName) { - var value = null; - - if (detailPageName === undefined) { - if (this._detailPage && this._detailPage.name) - detailPageName = this._detailPage.name; - } - - if (typeof($) == 'function' && detailPageName) { - field = $("#" + detailPageName + " #" + fieldName); - if (!field || field.length === 0) - field = $("#" + detailPageName + ' [dsid="' + fieldName + '"]'); - if (field && field.length == 1) - value = field.val(); - } - else { - field = document.getElementById(fieldName); - if (field) { - value = field.value; - } - } - - return value; - }; - - this._setFormField = function (fieldName, value, detailPageName) { - var field = null; - - if (detailPageName === undefined) { - if (this._detailPage && this._detailPage.name) - detailPageName = this._detailPage.name; - } - - if (typeof($) == 'function' && detailPageName) { - field = $("#" + detailPageName + " #" + fieldName); - if (!field || field.length === 0) - field = $("#" + detailPageName + ' [dsid="' + fieldName + '"]'); - if (field && field.length == 1) - field.val(value); - } - else { - field = document.getElementById(fieldName); - if (field) { - field.value = value; - } - } - }; - - /* - * Assigns field values from the form. - */ - this.assign = function (detailPageName) { - if (!this._tableRef.record) - throw new Error(msg.getMsgText("jsdoMSG002", this._tableRef._name)); - if ((arguments.length !== 0) && (typeof detailPageName != 'string')) - throw new Error(msg.getMsgText("jsdoMSG024", "UIHelper", "assign()")); - - // Ensure creation of before image record - this._tableRef.record.assign(null); - - var fieldName; - var schema = this._tableRef.getSchema(); - for (var i = 0; i < schema.length; i++) { - fieldName = schema[i].name; - if (fieldName == '_id') continue; - var value = this._getFormFieldValue(fieldName, detailPageName); - // CR OE00241289 Should always copy over field value unless undefined, - // user may have explicitly set it to blank - if (typeof value != 'undefined') { - if (typeof value == 'string' && schema[i].type != 'string') { - value = this._tableRef._jsdo._convertType(value, - schema[i].type, - schema[i].items ? schema[i].items.type : null); - } - this._tableRef.record.data[fieldName] = value; - } - } - - // Ensure order of record - this._tableRef.record._sortRecord(); - - return true; - }; - - this.display = function (pageName) { - if (!this._tableRef.record) - throw new Error(msg.getMsgText("jsdoMSG002", this._tableRef._name)); - - // Display record to form - var schema = this._tableRef.getSchema(); - for (var i = 0; i < schema.length; i++) { - this._setFormField(schema[i].name, this._tableRef.record.data[schema[i].name], pageName); - } - this._setFormField('_id', this._tableRef.record.data._id, pageName); - }; - - this.showListView = function () { - if (!this._listview) return; - - var uiTableRef = this; - var listviewElement; - if (typeof($) == 'function') { - listviewElement = $("#" + this._listview.name); - if (listviewElement && listviewElement.length == 1) { - listviewElement.html(this._listviewContent ? this._listviewContent : ''); - try { - if (listviewElement.attr("data-filter") === "true" - && typeof listviewElement.filterable === "function") { - listviewElement.filterable("refresh"); - } - else { - listviewElement.listview("refresh"); - } - } - catch (e) { - // Workaround for issue with JQuery Mobile throwning exception on refresh - } - } - - if (this._listview.autoLink) { - // Add trigger for 'tap' event to items - $("#" + this._listview.name + " li").each( - function (/* index */) { - $(this).bind('click', - function (/* event, ui */) { - var jsrecord = uiTableRef.getListViewRecord(this); - uiTableRef.display(); - if (typeof(uiTableRef._listview.onSelect) == 'function') { - uiTableRef._listview.onSelect(event, this, jsrecord); - } - }); - }); - } - } - else { - listviewElement = document.getElementById(this._listview.name); - if (listviewElement) { - listviewElement.innerHTML = this._listviewContent; - } - - if (this._listview.autoLink) { - var element = document.getElementById(this._listview.name); - if (element && element.childElementCount > 0) { - for (var i = 0; i < element.children.length; i++) { - element.children[i].onclick = function () { - var jsrecord = uihelper.getListViewRecord(this); - uihelper.display(); - if (typeof(uiTableRef._listview.onSelect) == 'function') { - uiTableRef._listview.onSelect(event, this, jsrecord); - } - }; - } - } - } - } - - this._listviewContent = undefined; - }; - - this.getFormFields = function (fields) { - if (!this._tableRef._schema) - return ''; - if (!(fields instanceof Array)) - fields = null; - else { - var tmpFields = {}; - for (var i = 0; i < fields.length; i++) { - tmpFields[fields[i]] = fields[i]; - } - fields = tmpFields; - } - var htmltext; - if (!fields || fields['_id']) { - htmltext = ''; - } - else - htmltext = ''; - htmltext += '
'; - - for (var i = 0; i < this._tableRef._schema.length; i++) { - var fieldName = this._tableRef._schema[i].name; - if (fieldName == '_id') continue; - if (fieldName.length > 0 && fieldName.charAt(0) == '_') continue; - if (fields && fields[fieldName] === undefined) continue; - var fieldLabel = this._tableRef._schema[i].title ? - this._tableRef._schema[i].title : this._tableRef._schema[i].name; - var text = (this._detailPage && this._detailPage.fieldTemplate) ? - this._detailPage.fieldTemplate : progress.ui.UIHelper._fieldTemplate; - text = text.replace(new RegExp('{__label__}', 'g'), fieldLabel); - text = text.replace(new RegExp('{__name__}', 'g'), this._tableRef._schema[i].name); - htmltext += text; - } - htmltext += '
'; - fields = null; - return htmltext; - }; - - this.getListViewRecord = function (htmlIElement) { - var id = htmlIElement.getAttribute('data-id'); - return this._tableRef.findById(id); - }; - - this.getFormRecord = function (detailPageName) { - var id = this._getFormFieldValue('_id', detailPageName); - return this._tableRef.findById(id); - }; - - this._getIdOfElement = function (name) { - if (typeof($) == 'function') { - var element = $("#" + name); - if (!element || element.length === 0) { - element = $('[dsid="' + name + '"]'); - if (element && element.length == 1) { - var id = element.attr("id"); - if (id) - return id; - } - } - } - return name; - }; - - this.setDetailPage = function setDetailPage(obj) { - if (!obj || (typeof(obj) != 'object')) - throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "object")); - if (!obj.name || (typeof(obj.name) != 'string')) - throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "name")); - this._detailPage = obj; - this._detailPage.name = this._getIdOfElement(this._detailPage.name); - }; - this.setListView = function setListView(obj) { - if (!obj || (typeof(obj) != 'object')) - throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "object")); - if (!obj.name || (typeof(obj.name) != 'string')) - throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "name")); - if (obj.format && (typeof(obj.name) != 'string')) - throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "format")); - - this._listview = obj; - this._listview.name = this._getIdOfElement(this._listview.name); - if (!this._listview.format) { - if (typeof($) == 'function') { - for (var i = 0; i < this._tableRef._schema.length; i++) { - var fieldName = this._tableRef._schema[i].name; - - field = $("#" + this._listview.name + ' [dsid="' + fieldName + '"]'); - if (field && field.length == 1) { - field.html('{' + fieldName + '}'); - } - } - } - var text = document.getElementById(this._listview.name).innerHTML; - var pos = text.indexOf('
'; - progress.ui.UIHelper._defaultFieldTemplate = '
' + - '' + - '
'; - progress.ui.UIHelper._itemTemplate = progress.ui.UIHelper._defaultItemTemplate; - progress.ui.UIHelper._fieldTemplate = progress.ui.UIHelper._defaultFieldTemplate; - - progress.ui.UIHelper.setItemTemplate = function (template) { - progress.ui.UIHelper._itemTemplate = template ? template : progress.ui.UIHelper._defaultItemTemplate; - }; - - progress.ui.UIHelper.setFieldTemplate = function (template) { - progress.ui.UIHelper._fieldTemplate = - template ? template : progress.ui.UIHelper._defaultFieldTemplate; - }; - -})(); - -//this is so that we can see the code in Chrome's Source tab when script is loaded via XHR -//# sourceURL=progress.jsdo.js -/* -progress.session.js Version: 4.4.1-2 - -Copyright (c) 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - /* define these if not defined yet - they may already be defined if - progress.js was included first */ - if (typeof progress === "undefined") { - progress = {}; - } - if (typeof progress.data === "undefined") { - progress.data = {}; - } - - progress.data.ServicesManager = {}; - progress.data.ServicesManager._services = []; - progress.data.ServicesManager._resources = []; - progress.data.ServicesManager._data = []; - progress.data.ServicesManager._sessions = []; - progress.data.ServicesManager._jsdosessions = []; - /* - progress.data.ServicesManager.put = function(id, jsdo) { - progress.data.ServicesManager._data[id] = jsdo; - }; - progress.data.ServicesManager.get = function(id) { - return progress.data.ServicesManager._data[id]; - }; - */ - - progress.data.ServicesManager.addResource = function (id, resource) { - if (progress.data.ServicesManager._resources[id] === undefined) - progress.data.ServicesManager._resources[id] = resource; - else - throw new Error("A resource named '" + id + "' was already loaded."); - }; - progress.data.ServicesManager.getResource = function (id) { - return progress.data.ServicesManager._resources[id]; - }; - progress.data.ServicesManager.addService = function (id, service) { - if (progress.data.ServicesManager._services[id] === undefined) - progress.data.ServicesManager._services[id] = service; - else - throw new Error("A service named '" + id + "' was already loaded."); - }; - progress.data.ServicesManager.getService = function (id) { - return progress.data.ServicesManager._services[id]; - }; - progress.data.ServicesManager.addSession = function (catalogURI, session) { - if (progress.data.ServicesManager._sessions[catalogURI] === undefined) - progress.data.ServicesManager._sessions[catalogURI] = session; - else - throw new Error("Cannot load catalog '" + catalogURI + "' multiple times."); - }; - - progress.data.ServicesManager.addJSDOSession = function (catalogURI, jsdosession) { - if (progress.data.ServicesManager._jsdosessions[catalogURI] === undefined) { - progress.data.ServicesManager._jsdosessions[catalogURI] = jsdosession; - } - else { - throw new Error("Cannot load catalog '" + catalogURI + "' multiple times."); - } - }; - progress.data.ServicesManager.getSession = function (catalogURI) { - try { - return progress.data.ServicesManager._sessions[catalogURI]; - } - catch (e) { - return null; - } - }; - - progress.data.ServicesManager.cleanSession = function (session) { - var servicesKey, - resourcesKey, - sessionsKey, - service, - services = progress.data.ServicesManager._services, - resources = progress.data.ServicesManager._resources, - sessions = progress.data.ServicesManager._sessions, - jsdosessions = progress.data.ServicesManager._jsdosessions; - - // Delete the services and resources in the ServicesManager - // associated with the Session given - for (servicesKey in services) { - service = null; - if (services[servicesKey]._session === session) { - service = services[servicesKey]; - delete services[servicesKey]; - } - - if (!service) { - continue; - } - - for (resourcesKey in resources) { - if (resources[resourcesKey].service === service) { - delete resources[resourcesKey]; - } - } - } - - // Delete the session and jsdosession from the ServicesManager - for (sessionsKey in sessions) { - if (sessions[sessionsKey] === session) { - delete sessions[sessionsKey]; - - if(jsdosessions[sessionsKey]) { - delete jsdosessions[sessionsKey]; - } - } - } - }; - - /* - * Scans URL for parameters of the form {name} - * Returns array with the names - */ - function extractParamsFromURL(url) { - var urlParams = []; - if (typeof(url) == 'string') { - var paramName = null; - for (var i = 0; i < url.length; i++) { - if (url.charAt(i) == '{') { - paramName = ""; - } - else if (url.charAt(i) == '}') { - if (paramName) - urlParams.push(paramName); - paramName = null; - } - else if (paramName !== null) { - paramName += url.charAt(i); - } - } - } - return urlParams; - } - - /* - * Adds the catalog.json file provided by the catalog parameter, which is a JSDO - * that has loaded the catalog - */ - progress.data.ServicesManager.addCatalog = function (services, session) { - if (!services) { - throw new Error("Cannot find 'services' property in catalog file."); - } - if (services instanceof Array) { - - // first check if there are duplicates before we add them to our cache, - // which only handles unique values - for (var j = 0; j < services.length; j++) { - // don't allow services with the same name across sessions - if (progress.data.ServicesManager.getService(services[j].name) !== undefined) - throw new Error("A service named '" + services[j].name + "' was already loaded."); - - var resources = services[j].resources; - - if (resources instanceof Array) { - for (var i = 0; i < resources.length; i++) { - if (progress.data.ServicesManager.getResource(resources[i].name) !== undefined) - throw new Error("A resource named '" + resources[i].name + - "' was already loaded."); - } - } - else { - throw new Error("Missing 'resources' array in catalog."); - } - } - - for (var j = 0; j < services.length; j++) { - services[j]._session = session; - this.addService(services[j].name, services[j]); // Register the service - var resources = services[j].resources; - var baseAddress = services[j].address; - if (resources instanceof Array) { - for (var i = 0; i < resources.length; i++) { - var resource = resources[i]; - resource.fn = {}; - resource.service = services[j]; - resources[i].url = baseAddress + resources[i].path; - // Register resource - progress.data.ServicesManager.addResource(resources[i].name, resources[i]); - - // Process schema - resource.fields = null; - resource.primaryKeys = null; - if (resource.schema) { - resource.fields = {}; - resource.primaryKeys = {}; - resource._dataSetName = undefined; - resource._tempTableName = undefined; - var properties = null; - - try { - if (typeof resource.schema.properties != 'undefined') { - var keys = Object.keys(resource.schema.properties); - properties = resource.schema.properties; - if (keys.length == 1) { - if (typeof resource.schema.properties[keys[0]].properties != - 'undefined') { - // Schema corresponds to a DataSet - resource._dataSetName = keys[0]; - } - else if (typeof resource.schema.properties[keys[0]].items != - 'undefined') { - // Schema corresponds to a temp-table - resource.dataProperty = keys[0]; - properties = resource.schema.properties[keys[0]].items.properties; - resource._tempTableName = resource.dataProperty; - resource.primaryKeys[resource._tempTableName] = - resource.schema.properties[keys[0]].primaryKey; - } - } - } - else { - var keys = Object.keys(resource.schema); - if (keys.length == 1) { - resource.dataProperty = keys[0]; - if (typeof resource.schema[keys[0]].items != 'undefined') { - // Catalog format correspond to Table Schema - properties = resource.schema[keys[0]].items.properties; - resource._tempTableName = resource.dataProperty; - resource.primaryKeys[resource._tempTableName] = - resource.schema[keys[0]].primaryKey; - } - else if (typeof resource.schema[keys[0]].properties != 'undefined') { - // Catalog format correspond to DataSet Schema - resource._dataSetName = keys[0]; - resource.dataProperty = null; - properties = resource.schema; - } - } - } - } - catch (e) { - throw new Error("Error parsing catalog file."); - } - if (properties) { - if (resource._dataSetName) { - properties = properties[resource._dataSetName].properties; - for (var tableName in properties) { - resource.fields[tableName] = []; - resource.primaryKeys[tableName] = properties[tableName].primaryKey; - var tableProperties; - if (properties[tableName].items - && properties[tableName].items.properties) { - tableProperties = properties[tableName].items.properties; - } - else { - tableProperties = properties[tableName].properties; - } - for (var field in tableProperties) { - tableProperties[field].name = field; - if (field != '_id') - resource.fields[tableName].push(tableProperties[field]); - } - } - } - else { - var tableName = resource.dataProperty ? resource.dataProperty : ""; - resource.fields[tableName] = []; - for (var field in properties) { - properties[field].name = field; - if (field != '_id') - resource.fields[tableName].push(properties[field]); - } - } - } - else - throw new Error("Error parsing catalog file."); - } - else - resource.fields = null; - - // Validate relationship property - if ((resource.relations instanceof Array) - && resource.relations[0] - && resource.relations[0].RelationName) { - throw new Error( - "Relationship properties in catalog must begin with lowercase."); - } - // Process operations - resource.generic = {}; - if (resource.operations) { - for (var idx = 0; idx < resource.operations.length; idx++) { - if (resource.operations[idx].path) { - resource.operations[idx].url = - resource.url + resource.operations[idx].path; - } - else { - resource.operations[idx].url = resource.url; - } - if (!resource.operations[idx].params) { - resource.operations[idx].params = []; - } - if (!resource.operations[idx].type) { - resource.operations[idx].type = "INVOKE"; - } - - // Set opname - validation of opname is done later - var opname = resource.operations[idx].type.toLowerCase(); - - // Set default verb based on operation - if (!resource.operations[idx].verb) { - switch (opname) { - case 'create': - resource.operations[idx].verb = "POST"; - break; - case 'read': - resource.operations[idx].verb = "GET"; - break; - case 'update': - case 'invoke': - case 'submit': - case 'count': - resource.operations[idx].verb = "PUT"; - break; - case 'delete': - resource.operations[idx].verb = "DELETE"; - break; - default: - break; - } - } - - // Point fn to operations - var func = function fn(object, async) { - var deferred; - - // Add static variable fnName to function - if (typeof fn.fnName == 'undefined') { - fn.fnName = arguments[0]; // Name of function - fn.definition = arguments[1]; // Operation definition - return; - } - - var reqBody = null; - var url = fn.definition.url; - var jsdo = this; - var xhr = null; - - var request = {}; - if (object) { - if (typeof(object) != "object") { - throw new Error("Catalog error: Function '" + - fn.fnName + "' requires an object as a parameter."); - } - var objParam; - if (object instanceof XMLHttpRequest - || (object.constructor - && object.constructor.name === "XMLHttpRequest")) { - jsdo = object.jsdo; - xhr = object; - objParam = xhr.objParam; - - // use the request from the xhr request if possible - request = xhr.request; - } - else { - objParam = object; - } - - if (typeof async == 'undefined') { - async = this._async; - } - else { - async = Boolean(async); - } - - request.objParam = objParam; - - - // Process objParam - var isInvoke = (fn.definition.type.toUpperCase() == 'INVOKE'); - for (var i = 0; i < fn.definition.params.length; i++) { - var name = fn.definition.params[i].name; - switch (fn.definition.params[i].type) { - case 'PATH': - case 'QUERY': - case 'MATRIX': - var value = null; - if (objParam) - value = objParam[name]; - if (!value) - value = ""; - if (url.indexOf('{' + name + '}') == -1) { - throw new Error("Catalog error: Reference to " + - fn.definition.params[i].type + " parameter '" + - name + "' is missing in path."); - } - url = url.replace( - new RegExp('{' + name + '}', 'g'), - encodeURIComponent(value)); - break; - case 'REQUEST_BODY': - case 'REQUEST_BODY,RESPONSE_BODY': - case 'RESPONSE_BODY,REQUEST_BODY': - if (xhr && !reqBody) { - reqBody = objParam; - } - else { - var reqParam = objParam[name]; - if (isInvoke - && (fn.definition.params[i].xType - && ("DATASET,TABLE".indexOf( - fn.definition.params[i].xType) != -1))) { - var unwrapped = (jsdo._resource.service.settings - && jsdo._resource.service.settings.unwrapped); - if (unwrapped) { - // Remove extra level if found - if ((typeof(reqParam) == 'object') - && (Object.keys(reqParam).length == 1) - && (typeof(reqParam[name]) == 'object')) - reqParam = reqParam[name]; - } - else { - // Add extra level if not found - if ((typeof(reqParam) == 'object') - && (typeof(reqParam[name])=='undefined')){ - reqParam = {}; - reqParam[name] = objParam[name]; - } - } - } - if (!reqBody) { - reqBody = {}; - } - reqBody[name] = reqParam; - } - break; - case 'RESPONSE_BODY': - break; - default: - throw new Error("Catalog error: " + - "Unexpected parameter type '" + - fn.definition.params[i].type + "'."); - } - } - - // URL has parameters - if (url.indexOf('{') != -1) { - var paramsFromURL = extractParamsFromURL(url); - for (var i = 0; i < paramsFromURL.length; i++) { - var name = paramsFromURL[i]; - var value = null; - if (objParam) - value = objParam[name]; - if (!value) - value = ""; - if (typeof(value) === "object") { - value = JSON.stringify(value); - } - url = url.replace( - new RegExp('{' + name + '}', 'g'), - encodeURIComponent(value)); - } - } - } - - request.fnName = fn.fnName; - request.async = async; - - if (request.deferred === undefined && - typeof($) == 'function' && typeof($.Deferred) == 'function') { - deferred = $.Deferred(); - request.deferred = deferred; - } - - var data = jsdo._httpRequest(xhr, fn.definition.verb, - url, reqBody, request, async); - return data; - }; - // End of Function Definition - - switch (resource.operations[idx].verb.toLowerCase()) { - case 'get': - case 'post': - case 'put': - case 'delete': - break; - default: - throw new Error("Catalog error: Unexpected HTTP verb '" + - resource.operations[idx].verb + - "' found while parsing the catalog."); - } - - switch (opname) { - case 'invoke': - break; - case 'create': - case 'read': - case 'update': - case 'delete': - case 'submit': - case 'count': - if (typeof(resource.generic[opname]) == "function") { - throw new Error("Catalog error: Multiple '" + - resource.operations[idx].type + - "' operations specified in the catalog for resource '" + - resource.name + "'."); - } - else - resource.generic[opname] = func; - break; - default: - throw new Error("Catalog error: Unexpected operation '" + - resource.operations[idx].type + - "' found while parsing the catalog."); - } - - // Set fnName - var name = resource.operations[idx].name; - if (opname === "invoke" || opname === "count") { - resource.fn[name] = {}; - resource.fn[name]["function"] = func; - } - else { - name = "_" + opname; - } - func(name, resource.operations[idx]); - } - } - } - } - } - } - else { - throw new Error("Missing 'services' array in catalog."); - } - - }; - - /* - * Prints debug information about the ServicesManager. - */ - progress.data.ServicesManager.printDebugInfo = function (resourceName) { - if (resourceName) { - //console.log("** ServicesManager **"); - //console.log("** BEGIN **"); - var resource = progress.data.ServicesManager.getResource(resourceName); - if (resource) { - var cSchema = "Schema:\n"; - var cOperations = "Operations: " + resource.operations.length + "\n"; - for (var field in resource.schema.properties) { - cSchema += "\nName: " + field - + "\n"; - } - - for (var i = 0; i < resource.operations.length; i++) { - cOperations += "\n" + i - + "\nName: " + resource.operations[i].name - + "\nURL: " + resource.operations[i].url - + "\ntype: " + resource.operations[i].type - + "\nverb: " + resource.operations[i].verb - + "\nparams: " + resource.operations[i].params.length - + "\n"; - } - console.log("** DEBUG INFO **\nResource name: %s\nURL:%s\n%s\n%s\n\n", - resource.name, resource.url, cSchema, cOperations); - } - else - console.log("Resource not found"); - //console.log("** END **"); - } - }; - - - /* - * Contains information about a server-side Mobile service. - * Properties of args parameter for constructor: - * @param name the name of the service - * @param uri the URI of the service - */ - progress.data.MobileServiceObject = function MobileServiceObject(args) { - var _name = args.name; - Object.defineProperty(this, 'name', - { - get: function () { - return _name; - }, - enumerable: true - }); - - var _uri = args.uri; - Object.defineProperty(this, 'uri', - { - get: function () { - return _uri; - }, - enumerable: true - }); - }; - - /* - An object that maintains the X-CLIENT-PROPS header string - The data for the string is stored in the internal variable named contextObject and is - always up to date. The internal var contextString isn't created until the first time it's - needed (the first get of the contextHeader property), and then it's updated an cached - A call to setContext or setContextProperty updates contextObject but sets contextString to - null, which signals that it needs to be updated. If contextObject is an empty object, - contextString is set to undefined to indicate that no header is to be sent - */ - progress.data.ContextProperties = function() { - var contextObject = {}, - contextString; // if null, contextObject has been changed but string wasn't updated yet - - // the string to be sent in the X-CLIENT-PROPS header (unless Session.xClientProps has been set) - Object.defineProperty(this, 'contextHeader', - { - get: function () { - var header; - - if (contextString === null) { // needs to be updated - header = JSON.stringify( contextObject ); - if (header === "{}") { - contextString = undefined; - } - else { - contextString = header; - } - } - // else (contextString === undefined || has a usable value) - - return contextString; - }, - enumerable: true - }); - - /* determine whether the property is already present, and - - add it if it's not present - remove it if propertyValue is explicitly passed as undefined - otherwise replace its value (even if the new value is null or "") - */ - this.setContextProperty = function( propertyName, propertyValue) { - if (arguments.length < 2) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', - 'setContextProperty', 2)); - } - if (arguments.length !== 2) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", "Session", - "setContextProperty", 2)); - } - if (typeof propertyName !== "string") { - // {1}: Parameter {1} must be of type {3} in {4} call. - throw new Error(progress.data._getMsgText("jsdoMSG121", 'Session', 1, 'string', - 'setContextProperty')); - } - - if ( propertyValue === undefined ) { - delete contextObject[propertyName]; // OK if it doesn't exist -- no error - } - else { - contextObject[propertyName] = propertyValue; - } - contextString = null; // must be updated on next get of this.contextHeader - }; - - this.setContext = function( context ) { - var prop; - - if (arguments.length < 1) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'setContext', 1)); - } - if (arguments.length > 1) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'setContext', 1)); - } - if ( typeof context == "object" ) { - /* Copy the properties of the context passed in as an argument into - * an internal contextObject. (Note that if the context object passed in - * has a prototype, this code copies them, too) - */ - contextObject = {}; - for (prop in context) { - if( context.hasOwnProperty(prop) ) { - if (typeof context[prop] !== "function" ) { - contextObject[prop] = context[prop]; - } - } - } - } - else if ( (context === undefined) || (context === null) ) { - contextObject = {}; - } - else { - // {1}: Parameter {1} must be of type {3} in {4} call. - throw new Error(progress.data._getMsgText("jsdoMSG121", 'Session', 1, 'Object', - 'setContextProperty')); - } - contextString = null; // must be updated on next get of this.contextHeader - }; - - this.getContext = function( ) { - if (arguments.length > 0) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContext', 0)); - } - return contextObject; - }; - - this.getContextProperty = function( propertyName) { - if (arguments.length < 1) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContextProperty', 1)); - } - if (arguments.length > 1) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContextProperty', 1)); - } - return contextObject[propertyName]; - }; - - }; // end of ContextProperties - - /* - * Manages authentication and session ID information for a service. - * - * Use: OE mobile developer instantiates a session and calls addCatalog() to load - * information for one or more services defined in a catalog file. - * - * Developer instantiates JDSOs as needed. - * Usually all of the JSDOs will use the same session, but if a client-side - * service needs resources from more than one REST app, there would need to be more - * than one session - * - */ - progress.data.Session = function Session(options) { - - var defPropSupported = false; - if ((typeof Object.defineProperty) == 'function') { - defPropSupported = true; - } - - var that = this, - jsdosession, // "backpointer" if this Session is being used by a JSDOSession - isUserAgentiOS = false, // checked just below this var statement - isFirefox = false, // checked just below this var statement - isEdge = false, // checked just below this var statement - isIE = false, // checked just below this var statement - canPassCredentialsToOpenWithCORS = false, // False will always work if creds are correct - defaultiOSBasicAuthTimeout = 4000, - deviceIsOnline = true, // online until proven offline - restApplicationIsOnline = false, // was the Mobile Web Application that this Session object - // connects to online the last time it was checked? - // (value is always false if session is not logged in) - oepingAvailable = false, - defaultPartialPingURI = "/rest/_oeping", - partialPingURI = defaultPartialPingURI, - _storageKey, - _authProvider = null, - customCredentials = false, - - // Note: the variables above here are used during the lifetime of the object; the ones below - // are only used while the constructor is executing - storedAuthModel, - storedURI, - newURI, - stateWasReadFromStorage = false; - - // This is a hidden argument to suppress this warning and be re-used for future warnings - if (!options || options._silent !== true) { - console.warn("Session: As of JSDO 4.4, the Session object has been deprecated. Please use the JSDOSession object instead."); - } - - if (typeof navigator !== "undefined") { - if (typeof navigator.userAgent !== "undefined") { - isUserAgentiOS = navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)/i); - isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; - // detect that we're running in MS Edge browser - isEdge = navigator.userAgent.indexOf('Edge/') > -1; - // detect that we're running in IE 11 (or IE 11 in pre-11 mode) or IE 10 browser - isIE = ( (navigator.userAgent.indexOf('Trident/')) > -1 || (navigator.userAgent.indexOf('MSIE 10') > -1)); - } - } - - // Firefox, Edge, and IE will throw an error on the send() if CORS is being used for the request - // and we have included credentials in the URI (which is what passing them to open() does), - canPassCredentialsToOpenWithCORS = !(isFirefox || isEdge || isIE); - - // When using basic authentication, we can pass the user name and password to the XMLHttpRequest.open() - // method. However, in some browsers, passing credentials to open() will result in the xhr's .send() - // method throwing an error. The goal of this function is to figure out whether it's safe to include - // the credentials. It returns false if there could be a problem, true otherwise. - // Note: currently it does this solely on the basis of what browser we are running in, regardless - // of whether the request will actually use the CORS protocol. Ideally, we should take into account whether - // the request will actually require CORS. The question is whether we can reliably do that. - // The reason for taking the specific request into account is that there are drawbacks to not passing the - // credentials when we are NOT using CORS, namely that if the credentials are invalid, some browsers will - // put up their own prompt for credentials in non-CORS situations (those browsers are IE, Edge, and Chrome) - function canPassCredentialsToOpen() { - return canPassCredentialsToOpenWithCORS; - } - - this._onlineHandler = function () { - setDeviceIsOnline(true); - that.trigger("online", that, null); - }; - - this._offlineHandler = function () { - setDeviceIsOnline(false); - that.trigger("offline", that, progress.data.Session.DEVICE_OFFLINE, null); - }; - - if ((typeof window != 'undefined' ) && (window.addEventListener)) { - window.addEventListener("online", this._onlineHandler, false); - window.addEventListener("offline", this._offlineHandler, false); - } - - /* constants and properties - define them as properties via the defineProperty() - * function, which has "writable" and "configurable" parameters that both - * default to false, so these calls create properties that are read-only - * - * IF WE DECIDE THAT WE CAN ASSUME WE ALWAYS RUN WITH A VERSION OF JAVASCRIPT THAT SUPPORTS - * Object.DefineProperty(), WE CAN DELETE THE defPropSupported VARIABLE, THE TEST OF IT BELOW, - * AND THE 'ELSE' CLAUSE BELOW AND ALL THE setXxxx functions (AND CHANGE THE CALLS TO THE setXxxx - * FUNCTIONS SO THEY JUST REFER TO THE PROPERTY) - * - */ - - // define these unconditionally so we don't get a warning on the push calls that they might - // have been uninitialized - var _catalogURIs = []; - var _services = []; - var _jsdos = []; - - this.onOpenRequest = null; - - var _password = null; - - if (defPropSupported) { - var _userName = null; - Object.defineProperty(this, 'userName', - { - get: function () { - return _userName; - }, - enumerable: true - }); - - var _loginTarget = '/static/home.html'; - Object.defineProperty(this, 'loginTarget', - { - get: function () { - return _loginTarget; - }, - enumerable: true - }); - - var _serviceURI = null; - Object.defineProperty(this, 'serviceURI', - { - get: function () { - return _serviceURI; - }, - enumerable: true - }); - - Object.defineProperty(this, 'catalogURIs', - { - get: function () { - return _catalogURIs; - }, - enumerable: true - }); - - Object.defineProperty(this, 'services', - { - get: function () { - return _services; - }, - enumerable: true - }); - - var _loginResult = null; - Object.defineProperty(this, 'loginResult', - { - get: function () { - return _loginResult; - }, - enumerable: true - }); - - var _loginHttpStatus = null; - Object.defineProperty(this, 'loginHttpStatus', - { - get: function () { - return _loginHttpStatus; - }, - enumerable: true - }); - - var _clientContextId = null; - Object.defineProperty(this, 'clientContextId', - { - get: function () { - return _clientContextId; - }, - enumerable: true - }); - - var _authenticationModel = progress.data.Session.AUTH_TYPE_ANON; - Object.defineProperty(this, 'authenticationModel', - { - get: function () { - return _authenticationModel; - }, - set: function (newval) { - if (newval) { - newval = newval.toLowerCase(); - } - switch (newval) { - case progress.data.Session.AUTH_TYPE_FORM : - case progress.data.Session.AUTH_TYPE_BASIC : - case progress.data.Session.AUTH_TYPE_ANON : - case progress.data.Session.AUTH_TYPE_SSO : - case null : - _authenticationModel = newval; - storeSessionInfo("authenticationModel", newval); - break; - default: - throw new Error("Error setting Session.authenticationModel. '" + - newval + "' is an invalid value."); - } - }, - enumerable: true - }); - - var _lastSessionXHR = null; - Object.defineProperty(this, 'lastSessionXHR', - { - get: function () { - return _lastSessionXHR; - }, - enumerable: true - }); - - Object.defineProperty(this, 'connected', - { - get: function () { - return (this.loginResult === progress.data.Session.LOGIN_SUCCESS) - && restApplicationIsOnline - && deviceIsOnline; - }, - enumerable: true - }); - - Object.defineProperty(this, 'JSDOs', - { - get: function () { - return _jsdos; - }, - enumerable: true - }); - - var _pingInterval = 0; - var _timeoutID = null; - Object.defineProperty(this, 'pingInterval', - { - get: function () { - return _pingInterval; - }, - set: function (newval) { - if ( (typeof newval === "number") && (newval >= 0) ) { - _pingInterval = newval; - storeSessionInfo("pingInterval", newval); - if (newval > 0) { - // if we're logged in, start autopinging - if (this.loginResult === progress.data.Session.LOGIN_SUCCESS) { - _timeoutID = setTimeout(this._autoping, newval); - } - } - else if (newval === 0) { - clearTimeout(_timeoutID); - _pingInterval = 0; - } - } - else { - throw new Error("Error setting Session.pingInterval. '" + - newval + "' is an invalid value."); - } - }, - enumerable: true - }); - - var _contextProperties = new progress.data.ContextProperties(); - Object.defineProperty( this, - "_contextProperties", - { - get: function () { - return _contextProperties; - }, - enumerable: false - } - ); - - var isInvalidated = false; - Object.defineProperty( - this, - "_isInvalidated", - { - get: function () { - return isInvalidated; - }, - enumerable: false - } - ); - - // used internally, not supported as part of the Session API (tho authProvider is part - // of the *JSDOSession* API) - Object.defineProperty( this, - "_authProvider", - { - get: function () { - return _authProvider; - }, - set: function(newval) { - if (_authProvider) { - throw new Error("Internal Error setting Session._authProvider. '" + - "The property has already been set."); - - } else { - setAuthProvider(newval); - } - }, - enumerable: false - } - ); - } - else { - this.userName = null; - this.loginTarget = '/static/home.html'; - this.serviceURI = null; - this.catalogURIs = []; - this.services = []; - this.loginResult = null; - this.loginHttpStatus = null; - this.clientContextId = null; - this.authenticationModel = progress.data.Session.AUTH_TYPE_ANON; - this.lastSessionXHR = null; - } - - // stores data value using the JSDOSession's storage key plus the infoName - // argument as a key. If there is no infoName, just uses the storage key - // by itself (the latter case is intended to serve as a flag that we have - // stored this JSDOSession's data before) - // - function storeSessionInfo(infoName, value) { - var key; - if (that.loginResult === progress.data.Session.LOGIN_SUCCESS && - typeof (sessionStorage) === 'object' && _storageKey) { - - key = _storageKey; - if (infoName) { - key = key + "." + infoName; - } - if (typeof (value) !== 'undefined') { - sessionStorage.setItem(key, JSON.stringify(value)); - } - } - } - - function retrieveSessionInfo(infoName) { - var key, - jsonStr, - value = null; - if (typeof (sessionStorage) === 'object' && _storageKey) { - key = _storageKey; - if (infoName) { - key = key + "." + infoName; - } - jsonStr = sessionStorage.getItem(key); - if (jsonStr !== null) { - try { - value = JSON.parse(jsonStr); - } catch (e) { - value = null; - } - } - return value; - } - } - - function clearSessionInfo(infoName) { - var key; - if (typeof (sessionStorage) === 'object' && _storageKey) { - key = _storageKey; - if (infoName) { - key = key + "." + infoName; - sessionStorage.removeItem(key); - } - } - } - - function storeAllSessionInfo() { - if (_storageKey) { - storeSessionInfo("loginResult", that.loginResult); - storeSessionInfo("userName", that.userName); - storeSessionInfo("serviceURI", that.serviceURI); - storeSessionInfo("loginHttpStatus", that.loginHttpStatus); - storeSessionInfo("authenticationModel", that.authenticationModel); - storeSessionInfo("pingInterval", that.pingInterval); - storeSessionInfo("oepingAvailable", oepingAvailable); - storeSessionInfo("partialPingURI", partialPingURI); - storeSessionInfo("clientContextId", that.clientContextId); - storeSessionInfo("deviceIsOnline", deviceIsOnline); - storeSessionInfo("restApplicationIsOnline", restApplicationIsOnline); - if (that._authProvider) { - storeSessionInfo("_authProvider.init", - {uri: that._authProvider.uri, - authenticationModel: that._authProvider.authenticationModel}); - } - storeSessionInfo(_storageKey, true); - } - } - - function clearAllSessionInfo() { - if (_storageKey) { - if (retrieveSessionInfo(_storageKey)) { - clearSessionInfo("loginResult"); - clearSessionInfo("userName"); - clearSessionInfo("serviceURI"); - clearSessionInfo("loginHttpStatus"); - clearSessionInfo("clientContextId"); - clearSessionInfo("deviceIsOnline"); - clearSessionInfo("restApplicationIsOnline"); - clearSessionInfo("authenticationModel"); - clearSessionInfo("pingInterval"); - clearSessionInfo("oepingAvailable"); - clearSessionInfo("partialPingURI"); - clearSessionInfo("_authProvider.init"); - clearSessionInfo(_storageKey); - } - } - } - - function setSessionInfoFromStorage(key) { - var authproviderInitObject, - authProvider; - if (retrieveSessionInfo(key)) { - setLoginResult(retrieveSessionInfo("loginResult"), this); - setUserName(retrieveSessionInfo("userName"), this); - setServiceURI(retrieveSessionInfo("serviceURI"), this); - setLoginHttpStatus(retrieveSessionInfo("loginHttpStatus"), this); - setClientContextID(retrieveSessionInfo("clientContextId"), this); - setDeviceIsOnline(retrieveSessionInfo("deviceIsOnline")); - setRestApplicationIsOnline(retrieveSessionInfo("restApplicationIsOnline")); - that.authenticationModel = retrieveSessionInfo("authenticationModel"); - that.pingInterval = retrieveSessionInfo("pingInterval"); - setOepingAvailable(retrieveSessionInfo("oepingAvailable")); - setPartialPingURI(retrieveSessionInfo("partialPingURI")); - // if information on an AuthenticationProvider for the session is in storage, and if - // the authProvider hasn't already been set for this Session, create a new authProvider - // using the same info as the old one. This would be likely to happen if the app's code - // had used the old JSDOSession.login API, where we create the AuthenticationProvider - // automatically during login instead of the code passing one to the constructor - if (!that._authProvider) { - authproviderInitObject = retrieveSessionInfo("_authProvider.init"); - if (authproviderInitObject) { - setAuthProvider(new progress.data.AuthenticationProvider(authproviderInitObject)); - } - } - } - } - - function setUserName(newname, sessionObject) { - if (defPropSupported) { - _userName = newname; - } - else { - sessionObject.userName = newname; - } - - storeSessionInfo("userName", newname); - } - - function setLoginTarget(target, sessionObject) { - if (defPropSupported) { - _loginTarget = target; - } - else { - sessionObject.loginTarget = target; - } - } - - function setServiceURI(url, sessionObject) { - if (defPropSupported) { - _serviceURI = url; - } - else { - sessionObject.serviceURI = url; - } - - storeSessionInfo("serviceURI", url); - } - - function pushCatalogURIs(url, sessionObject) { - if (defPropSupported) { - _catalogURIs.push(url); - } - else { - sessionObject.catalogURIs.push(url); - } - } - - function pushService(serviceObject, sessionObject) { - if (defPropSupported) { - _services.push(serviceObject); - } - else { - sessionObject.services.push(serviceObject); - } - } - - function findService(serviceName) { - for (var prop in _services) { - var srv = _services[prop]; - if (srv.name === serviceName) { - return srv; - } - } - return null; - } - - function setLoginResult(result, sessionObject) { - if (defPropSupported) { - _loginResult = result; - } else { - sessionObject.loginResult = result; - } - - if (result === progress.data.Session.LOGIN_SUCCESS) { - storeSessionInfo("loginResult", result); - } else { - // Let's clear sessionStorage since we logged out or something went bad! - clearAllSessionInfo(); - } - } - - function setLoginHttpStatus(status, sessionObject) { - if (defPropSupported) { - _loginHttpStatus = status; - } - else { - sessionObject.loginHttpStatus = status; - } - - storeSessionInfo("loginHttpStatus", status); - } - - function setClientContextIDfromXHR(xhr, sessionObject) { - if (xhr) { - setClientContextID(getResponseHeaderNoError(xhr, "X-CLIENT-CONTEXT-ID"), sessionObject); - } - } - - function setClientContextID(ccid, sessionObject) { - if (defPropSupported) { - _clientContextId = ccid; - } - else { - sessionObject.clientContextId = ccid; - } - - storeSessionInfo("clientContextId", ccid); - } - - function setLastSessionXHR(xhr, sessionObject) { - if (defPropSupported) { - _lastSessionXHR = xhr; - } - else { - sessionObject.lastSessionXHR = xhr; - } - } - - function setDeviceIsOnline(value) { - deviceIsOnline = value; - - storeSessionInfo("deviceIsOnline", value); - } - - function setAuthProvider(value) { - // Do this to preserve authprovider's null-ness. - _authProvider = value ? value : null; - } - - function setRestApplicationIsOnline(value) { - restApplicationIsOnline = value; - - storeSessionInfo("restApplicationIsOnline", value); - } - - function setOepingAvailable(value) { - oepingAvailable = value; - - storeSessionInfo("oepingAvailable", value); - } - - function setPartialPingURI(value) { - partialPingURI = value; - - storeSessionInfo("partialPingURI", value); - } - - /* - When using CORS, if the client asks for a response header that is not among - the headers exposed by the Web application, the user agent may write an error - to the console, e.g., "REFUSED TO GET UNSAFE HEADER". This function checks for - a given response header in a way that will avoid the error message. It does this - by requesting all headers and then checking to see whether the desired header - is present (it will not be present, even if the server sent it, if the server has not - also allowed that header). The function caches the string returned by getAllResponseHeaders - by storing it on the xhr that was used in the request. It does the caching in - case there is another header to be checked. - */ - function getResponseHeaderNoError(xhr, headerName) { - var allHeaders = xhr._pdsResponseHeaders, - regExp; - - if (allHeaders === undefined) { - allHeaders = xhr.getAllResponseHeaders(); - if ( allHeaders ) { - xhr._pdsResponseHeaders = allHeaders; - } - else { - xhr._pdsResponseHeaders = null; - } - } - if ( allHeaders ) { - regExp = new RegExp("^" + headerName + ":", "m"); - if ( allHeaders.match(regExp) ) { - return xhr.getResponseHeader(headerName); - } - } - - return null; - } - - // "Methods" - - this._pushJSDOs = function (jsdo) { - _jsdos.push(jsdo); - }; - - - /* _openRequest (intended for progress.data library use only) - * calls open() for an xhr -- the assumption is that this is an xhr for a JSDO, and we need to add - * some session management information for the request, such as user credentials and a session ID if - * there is one - * - * The callback parameter is to support async calls --- it's possible that the call in here to - * _openRequestAndAuthorize will make an async request (for token refresh), so it's expected that - * callers will invoke _openRequest with a callback parameter for async execution - */ - this._openRequest = function (xhr, verb, url, async, callback) { - var urlPlusCCID, - that = this; - - function afterOpenAndAuthorize(xhr) { - // add CCID header - if (that.clientContextId && (that.clientContextId !== "0")) { - xhr.setRequestHeader("X-CLIENT-CONTEXT-ID", that.clientContextId); - } - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(that, xhr); - - if (typeof that.onOpenRequest === 'function') { - var params = { - "xhr": xhr, - "verb": verb, - "uri": urlPlusCCID, - "async": async, - "formPreTest": false, - "session": that - }; - that.onOpenRequest(params); - // xhr = params.xhr; //Note that, currently, this would have no effect in the caller. - } - if (callback) { - callback(); - } - } - - if (this._isInvalidated) { - // Session: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "Session")); - } - - if (this.loginResult !== progress.data.Session.LOGIN_SUCCESS && !this._authProvider && this.authenticationModel) { - throw new Error("Attempted to make server request when there is no active session."); - } - - // if resource url is not absolute, add the REST app url to the front - urlPlusCCID = this._prependAppURL(url); - - // add CCID as JSESSIONID query string to url - urlPlusCCID = this._addCCIDtoURL(urlPlusCCID); - - // add time stamp to the url - if (progress.data.Session._useTimeStamp) { - urlPlusCCID = progress.data.Session._addTimeStampToURL(urlPlusCCID); - } - - // should be able to remove this check and only do what's in the "if" when we no longer - // support calling the Session API directly (need to keep that now because tdriver, for - // one, uses the Session object, and uses it synchronously - if (this._authProvider) { - this._authProvider._openRequestAndAuthorize(xhr, - verb, - urlPlusCCID, - async, - afterOpenAndAuthorize); - } else { - this._setXHRCredentials(xhr, verb, urlPlusCCID, this.userName, _password, async); - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - _addWithCredentialsAndAccept(xhr, "application/json"); - } - afterOpenAndAuthorize(xhr); - } - - }; - - // callback used in login to determine whether ping is available on server - this.pingTestCallback = function (cbArgs) { - var foundOeping = cbArgs.pingResult ? true : false; - - setOepingAvailable(foundOeping); - }; - - // generic async callback, currently used by login(), addCatalog(), logout(), connect, and disconnect - this._onReadyStateChangeGeneric = function () { - var xhr = this; - var result; - var errorObject; - - clearTimeout(xhr._requestTimeout); // for the iOS Basic Auth bug - - if (xhr.readyState == 4) { - result = null; - errorObject = null; - - // initial processing of the response from the Web application - if ((typeof xhr.onResponseFn) == 'function') { - try { - result = xhr.onResponseFn(xhr); - // ( note that result will remain null if this is a logout() ) - } - catch (e) { - errorObject = e; - } - } - // handle the results of the processing (e.g., fire any events required) - if ((typeof xhr.onResponseProcessedFn) == 'function') { - if (!result) { - result = progress.data.Session.GENERAL_FAILURE; - } - xhr.onResponseProcessedFn(xhr.pdsession, result, errorObject, xhr); - } - } - }; - - // Intended only for internal use by the JSDO library - // NOTE: disconnect does not currently send a request to the Web application for the Anonymous or - // OE SSO models. It's conceivable, though unlikely, that it might. For that reason, the design is - // similar to the functions that DO make a server request. There is a "setup" function (this one) - // and a separate function to process the "result" (_processDisconnectResult, below). Currently the - // setup function is minimal and just calls _processDisconnectResult directly. If we ever do need to - // send a server request, _processDisconnectResult will be specified as the callback to be invoked - // from onReadyStateChangeGeneric. The possibility of this potential enhancement is the reason for - // the odd signature of _processDisconnectResult, which has a currently unused first parameter for - // the potential XHR. - this._disconnect = function (deferred) { - - // Note: we use the "no harm, no foul" approach for disconnect. If you aren't connected, it's - // regarded as a success rather than cause for throwing an error. - this._processDisconnectResult(null, deferred); - }; - - - // This is separate from _disconnect for cases in which _disconnect makes a server request. - // If there has been a server request, xhr should be valid and deferred will be undefined - // If there was no server request, xhr will be undefined and deferred will be valid. - // If this needs to be enhanced to support server requests, see _procesLoginResponse as - // a general model - // Probably the only time this function will be called as the result of a server request is with - // Form authentication, and even then it's questionable - this._processDisconnectResult = function (xhr, deferred) { - - this._reinitializeAfterLogout(this, progress.data.Session.SUCCESS); - this._disconnectComplete(this, progress.data.Session.SUCCESS, null, null, deferred); - }; - - this._disconnectComplete = function (pdsession, result, errObj, xhr, deferred) { - pdsession.trigger("afterDisconnect", pdsession, result, errObj, xhr, deferred); - }; - - - // GET RID OF progress.data.Session login CODE (AND RELATED) IF WE DROP SUPPORT FOR USING - // THE OLD progress.data.Session API DIRECTLY (mainly a problem for existing code (tdriver), - // or anyone who wants to call methods synchronously, which should be no one) - /* login - * - */ - - // store password here until successful login; only then do we store it in the Session object - var pwSave = null; - // store user name here until successful login; only then do we store it in the Session object - var unameSave = null; - this.login = function (serviceURI, loginUserName, loginPassword, loginTarget) { - var uname, - pw, - isAsync = false, - args = [], - deferred, - iOSBasicAuthTimeout, - uriForRequest; // "decorated" version of serviceURI, used to actually send the request - - pwSave = null; // in case these are left over from a previous login - unameSave = null; - - if (!defPropSupported) { - // this is here on the presumably slim chance that we're running with a - // version of JavaScript that doesn't support defineProperty (otherwise - // the lower casing will have already happened). When we decide that it's - // OK to remove our conditionalization of property definitions, we should - // get rid of this whole conditional - this.authenticationModel = this.authenticationModel.toLowerCase(); - } - - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // Session: Cannot call login() when authenticationModel is SSO. - // Please use the AuthenticationProvider object instead. - throw new Error(progress.data._getMsgText("jsdoMSG057", 'Session', 'login()')); - } - - if (this.loginResult === progress.data.Session.LOGIN_SUCCESS || this._authProvider) { - throw new Error("Attempted to call login() on a Session object that is already logged in."); - } - - if (arguments.length > 0) { - if (arguments[0] && typeof(arguments[0]) === 'object') { - // Note that arguments[0].serviceURI may be undefined because when the JSDOSession - // uses a Session internally, it passes serviceURI to the constructor. The other - // properties may be present, though - args[0] = arguments[0].serviceURI; - args[1] = arguments[0].userName; - args[2] = arguments[0].password; - args[3] = arguments[0].loginTarget; - args[4] = arguments[0].async; - - /* Special for JSDOSession: if this method was called by a JSDOSession object, - it passes deferred and jsdosession and we need to eventually attach them - to the XHR we use so that the promise created by the JSDOSession will work - correctly - */ - deferred = arguments[0].deferred; - - iOSBasicAuthTimeout = arguments[0].iOSBasicAuthTimeout; - if ( typeof iOSBasicAuthTimeout === 'undefined' ) { - iOSBasicAuthTimeout = defaultiOSBasicAuthTimeout; - } - else if (iOSBasicAuthTimeout && (typeof iOSBasicAuthTimeout != 'number')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'login', - 'The iOSBasicAuthTimeout argument was invalid.')); - } - } - else { - args = arguments; - } - } - - if (args.length > 0) { - if (args[0]) { - var restURLtemp = args[0]; - - // get rid of trailing '/' because appending service url that starts with '/' - // will cause request failures - if (restURLtemp[restURLtemp.length - 1] === "/") { - restURLtemp = restURLtemp.substring(0, restURLtemp.length - 1); - } - setServiceURI(restURLtemp, this); - } else if (!this.serviceURI) { - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); - throw new Error("Session.login() is missing the serviceURI argument."); - } - - if (args[1]) { - uname = args[1]; - } - - if (args[2]) { - pw = args[2]; - } - - if (args[3]) { - setLoginTarget(args[3], this); - } - - if (args[4]) { - if (typeof(args[4]) === 'boolean') { - isAsync = args[4]; - } - else { - throw new Error("Session.login() was passed an async setting that is not a boolean."); - } - } - } - else { - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); - throw new Error("Session.login() is missing the serviceURI argument."); - } - - // use these temp cred variables later; if login succeeds, we'll use them to set the - // real credentials - unameSave = uname; - pwSave = pw; - - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_ANON || - this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - /* anonymous should NOT have a username and password passed (this is - probably unnecessary because the XHR seems to send the request without - credentials first, then intercept the 401 if there is one and try again, - this time with credentials. Just making sure. - */ - /* For form authentication, we may as well not send the user name and password - * on this request, since we are just trying to test whether the authentication - * has already happened and they are therefore irrelevant - */ - uname = null; - pw = null; - } - - var xhr = new XMLHttpRequest(); - xhr.pdsession = this; - - try { - uriForRequest = this.serviceURI + this.loginTarget; - if (progress.data.Session._useTimeStamp) { - uriForRequest = progress.data.Session._addTimeStampToURL(uriForRequest); - } - this._setXHRCredentials(xhr, 'GET', uriForRequest, uname, pw, isAsync); - - progress.data.Session._setNoCacheHeaders(xhr); - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(this, xhr); - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - _addWithCredentialsAndAccept(xhr, - "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - } - - xhr._isAsync = isAsync; - if (isAsync) { - xhr.onreadystatechange = this._onReadyStateChangeGeneric; - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - xhr.onResponseFn = this._afterFormPretestLogin; - } - else { - xhr.onResponseFn = this._processLoginResult; - xhr.onResponseProcessedFn = this._loginComplete; - } - if ( this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC - && isUserAgentiOS - && iOSBasicAuthTimeout > 0 ) { - xhr._requestTimeout = setTimeout( function (){ - clearTimeout(xhr._requestTimeout); - xhr._iosTimeOutExpired = true; - xhr.abort(); - }, - iOSBasicAuthTimeout); - } - xhr._jsdosession = jsdosession; // in case the caller is a JSDOSession - xhr._deferred = deferred; // in case the caller is a JSDOSession - } - - if (typeof this.onOpenRequest === 'function') { - var isFormPreTest = false; - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - isFormPreTest = true; - } - - // set this here in case onOpenRequest checks it - setLastSessionXHR(xhr, this); - var params = { - "xhr": xhr, - "verb": "GET", - "uri": this.serviceURI + this.loginTarget, - "async": false, - "formPreTest": isFormPreTest, - "session": this - }; - this.onOpenRequest(params); - xhr = params.xhr; // just in case it has been changed - } - setLastSessionXHR(xhr, this); - xhr.send(null); - } - catch (e) { - clearTimeout(xhr._requestTimeout); - setLoginHttpStatus(xhr.status, this); - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); - unameSave = null; - pwSave = null; - throw e; - } - - if (isAsync) { - return progress.data.Session.ASYNC_PENDING; - } - else { - setLoginHttpStatus(xhr.status, this); - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - return (this._afterFormPretestLogin(xhr) ); - } - else { - return (this._processLoginResult(xhr) ); - } - } - }; - - - this._afterFormPretestLogin = function (xhr) { - var pdsession = xhr.pdsession; - setLoginHttpStatus(xhr.status, xhr.pdsession); - - var formLoginParams = { - "xhr": xhr, - "pw": pwSave, - "uname": unameSave, - "theSession": pdsession - }; - try { - return doFormLogin(formLoginParams); - } - catch (e) { - pwSave = null; - unameSave = null; - throw e; - } - }; - - /* doFormLogin - * This function handles logging in to a service that uses form-based authentication. It's separate - * from the main login function because it's long. One of the things it does is examine the - * response from an initial attempt to get the login target without credentials (done in the main - * login() function) to determine whether the user has already been authenticated. Although a - * current OE Mobile Web application (as of 5/30/2013) will return an error if authentication - * failed on a form login, previous versions and non-OE servers return a - * redirect to a login page and the user agent (browser or native wrapper) - * usually then fetches the redirect location and returns it along with a - * 200 Success status, when in fcat it was an authentication failure. Hence - * the need to analyze the response to try to figure out what we get back. - * - */ - function doFormLogin(args) { - var xhr = args.xhr; - var theSession = args.theSession; - var oldXHR; - - // check whether we got the OE REST Form based error response - var contentType = null; - var needAuth = false; - var params = { - "session": theSession, - "xhr": xhr, - "statusFromjson": null - }; - - contentType = xhr.getResponseHeader("Content-Type"); - - if (contentType && contentType.indexOf("application/json") >= 0) { - handleJSONLoginResponse(params); - if ( !params.statusFromjson - || (params.statusFromjson >= 400 && params.statusFromjson < 500) - ) { - needAuth = true; - } - else { - // either the response shows that we're already authenticated, or - // there's some error other than an authentication error - setLoginHttpStatus(params.statusFromjson, theSession); - } - } - else { - // need to do only 200 for async to work with MWA down - if (theSession.loginHttpStatus == 200) { - if (_gotLoginForm(xhr)) { - needAuth = true; - } - // else we are assuming we truly retrieved the login target and - // therefore we were previously authenticated - } - // else had an error, just return it - } - - if (needAuth) { - // create new XHR, because if this is an async call we don't want to - // confuse things by using this xhr to send another request while we're - // still processing its old request (this function, doFormLogin(), may - // have been called from onReadyStateChangeGeneric and it's conceivable - // that that function has more code to execute involving this xhr) - oldXHR = xhr; - xhr = new XMLHttpRequest(); - args.xhr = xhr; - params.xhr = xhr; - - // need to transfer any properties that the Session code stored in the - // the xhr that need to persist across the 2 requests made by a our - // login implementation for Form auth - xhr.pdsession = oldXHR.pdsession; - xhr._isAsync = oldXHR._isAsync; - xhr._deferred = oldXHR._deferred; // special for JSDOSession - xhr._jsdosession = oldXHR._jsdosession; // special for JSDOSession - - xhr.open('POST', theSession.serviceURI + "/static/auth/j_spring_security_check",xhr._isAsync); - xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); - xhr.setRequestHeader("Cache-Control", "max-age=0"); - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(theSession, xhr); - - _addWithCredentialsAndAccept(xhr, "application/json"); - - try { - - // Note: this gives a developer a way to change certain aspects of how we do the - // form-based login, but we will still be assuming that we are going directly to - // j_spring_security_check and including credentials in the body. They really should not - // try to change that. - // - if (typeof theSession.onOpenRequest === 'function') { - var cbparams = { - "xhr": xhr, - "verb": "POST", - "uri": theSession.serviceURI + "/static/auth/j_spring_security_check", - "async": xhr._isAsync, - "formPreTest": false, - "session": theSession - }; - theSession.onOpenRequest(cbparams); - xhr = cbparams.xhr; - } - - if (xhr._isAsync) { - xhr.onreadystatechange = theSession._onReadyStateChangeGeneric; - xhr.onResponseFn = theSession._afterFormLogin; - xhr.onResponseProcessedFn = theSession._loginComplete; - } - - // j_username=username&j_password=password&submit=Submit - xhr.send("j_username=" + encodeURIComponent(args.uname) + "&j_password=" + encodeURIComponent(args.pw) + "&submit=Submit"); - } - catch (e) { - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, theSession); - setLoginHttpStatus(xhr.status, theSession); - // null the temporary credentials variables - unameSave = null; - pwSave = null; - throw e; - } - - } - - if (xhr._isAsync && !needAuth) { - xhr.onResponseProcessedFn = theSession._loginComplete; - return theSession._afterFormLogin(xhr); - } - if (!xhr._isAsync) { - return theSession._afterFormLogin(xhr); - } - - } - - this._afterFormLogin = function (xhr) { - // check what we got - var theSession = xhr.pdsession; - var params = { - "session": theSession, - "xhr": xhr, - "statusFromjson": null - }; - var contentType = xhr.getResponseHeader("Content-Type"); - - if (contentType && contentType.indexOf("application/json") >= 0) { - handleJSONLoginResponse(params); - if (!params.statusFromjson) { - throw new Error( - "Internal OpenEdge Mobile client error handling login response. HTTP status: " + - xhr.status + "."); - } - else { - setLoginHttpStatus(params.statusFromjson, theSession); - } - } - else { - if (xhr.status === 200) { - // Was the response actually the login failure page or the login page itself (in case - // the appSecurity config file sets the login failure url so the server sends the login - // page again)? If so, call it an error because the credentials apparently failed to be - // authenticated - if (_gotLoginFailure(xhr) || _gotLoginForm(xhr)) { - setLoginHttpStatus(401, theSession); - } - else { - setLoginHttpStatus(xhr.status, theSession); - } - } - } - - return theSession._processLoginResult(xhr); - }; - - - this._processLoginResult = function (xhr) { - /* OK, one way or another, by hook or by crook, the Session object's loginHttpStatus - * has been set to the value that indicates the real outcome of the - * login, after adjusting for form-based authentication and anything - * else. At this point, it should be just a matter of examining - * this.loginHttpStatus, using it to set this.loginResult, maybe doing - * some other work appropriate to the outcome of the login, and returning - * this.loginResult. - */ - var pdsession = xhr.pdsession; - - setLoginHttpStatus(xhr.status, xhr.pdsession); - - if (pdsession.loginHttpStatus === 200) { - setLoginResult(progress.data.Session.LOGIN_SUCCESS, pdsession); - setRestApplicationIsOnline(true); - setUserName(unameSave, pdsession); - _password = pwSave; - pdsession._saveClientContextId(xhr); - storeAllSessionInfo(); // save info to persistent storage - - var pingTestArgs = { - pingURI: null, async: true, onCompleteFn: null, - fireEventIfOfflineChange: true, onReadyStateFn: pdsession._pingtestOnReadyStateChange - }; - pingTestArgs.pingURI = pdsession._makePingURI(); - pdsession._sendPing(pingTestArgs); // see whether the ping feature is available - } - else { - if (pdsession.loginHttpStatus == 401) { - setLoginResult(progress.data.Session.LOGIN_AUTHENTICATION_FAILURE, pdsession); - } - else { - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, pdsession); - } - } - setLastSessionXHR(xhr, pdsession); - updateContextPropsFromResponse(pdsession, xhr); - - // null the temporary credentials variables - unameSave = null; - pwSave = null; - if (xhr._iosTimeOutExpired) { - throw new Error( progress.data._getMsgText("jsdoMSG047", "login") ); - } - - // return loginResult even if it's an async operation -- the async handler - // (e.g., onReadyStateChangeGeneric) will just ignore - return pdsession.loginResult; - }; - - - this._loginComplete = function (pdsession, result, errObj, xhr) { - pdsession.trigger("afterLogin", pdsession, result, errObj, xhr); - }; - - // GET RID OF progress.data.Session logout CODE (AND RELATED) IF WE DROP SUPPORT FOR USING - // THE OLD progress.data.Session API DIRECTLY (mainly a problem for existing code (tdriver), - // or anyone who wants to call methods synchronously, which should be no one) - this.logout = function (args) { - var isAsync = false, - errorObject = null, - xhr, - deferred, - params; - - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // Session: Cannot call logout() when authenticationModel is SSO. - // Please use the AuthenticationProvider object instead. - throw new Error(progress.data._getMsgText("jsdoMSG057", 'Session', 'logout()')); - } - - if (this.loginResult !== progress.data.Session.LOGIN_SUCCESS && this.authenticationModel) { - throw new Error("Attempted to call logout when there is no active session."); - } - - if (typeof(args) === 'object') { - isAsync = args.async; - if (isAsync && (typeof isAsync !== 'boolean')) { - throw new Error( progress.data._getMsgText("jsdoMSG033", - "Session", - 'logout', - 'The async argument was invalid.')); - } - /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes - deferred and jsdosession and we need to eventually attach them to the XHR we use - so that the promise created by the JSDOSession will work correctly - */ - deferred = args.deferred; - } - - xhr = new XMLHttpRequest(); - xhr.pdsession = this; - try { - /* logout when auth model is anonymous is a no-op on the server side - (but we need to set _jsdosession and _deferred anyway to make promise work - if logout was called by a JSDOSession) */ - xhr._jsdosession = jsdosession; // in case the caller is a JSDOSession - xhr._deferred = deferred; // in case the caller is a JSDOSession - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM || - this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { - if (isAsync) { - xhr.onreadystatechange = this._onReadyStateChangeGeneric; - xhr.onResponseFn = this._processLogoutResult; - xhr.onResponseProcessedFn = this._logoutComplete; - } - - - xhr.open('GET', this.serviceURI + "/static/auth/j_spring_security_logout", isAsync); - - /* instead of calling _addWithCredentialsAndAccept, we code the withCredentials - * and setRequestHeader inline so we can do it slightly differently. That - * function deliberately sets the request header inside the try so we don't - * run into a FireFox oddity that would give us a successful login and then - * a failure on getCatalog (see the comment on that function). On logout, - * however, we don't care -- just send the Accept header so we can get a 200 - * response - */ - try { - xhr.withCredentials = true; - } - catch (e) { - } - - xhr.setRequestHeader("Accept", "application/json"); - - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(this, xhr); - - if (typeof this.onOpenRequest === 'function') { - setLastSessionXHR(xhr, this); - params = { - "xhr": xhr, - "verb": "GET", - "uri": this.serviceURI + "/static/auth/j_spring_security_logout", - "async": false, - "formPreTest": false, - "session": this - }; - this.onOpenRequest(params); - xhr = params.xhr; - } - - setLastSessionXHR(xhr, this); - xhr.send(); - } - else { - xhr._anonymousLogoutOK = true; - } - } - catch (e) { - this._reinitializeAfterLogout(this, false); - throw e; - } - - if (!isAsync) { - try { - this._processLogoutResult(xhr); - } - catch (e) { - throw e; - } - } - - if (isAsync && this.authenticationModel === progress.data.Session.AUTH_TYPE_ANON) { - // fake async for Anonymous -- fire afterLogout event - try { - this._processLogoutResult(xhr); - } - catch (e) { - errorObject = e; - } - this._logoutComplete(this, null, errorObject, xhr); - } - - }; - - // This function erases all evidence of itself from the ServicesManager and - // flips a bit to prevent it to be used in the future - this.invalidate = function () { - isInvalidated = true; - cleanServicesManager(); - }; - - this._logoutComplete = function (pdsession, result, errorObject, xhr) { - // ignore result, it doesn't apply to logout -- is probably null or GENERAL_FAILURE - // we include it so onReadyStateChangeGeneric calls this correctly - pdsession.trigger("afterLogout", pdsession, errorObject, xhr); - }; - - this._processLogoutResult = function (xhr) { - var logoutSucceeded; - var pdsession = xhr.pdsession; - var basicStatusOK = false; - - if (xhr._anonymousLogoutOK) { - logoutSucceeded = true; - } - else if (xhr.status !== 200) { - /* Determine whether an error returned from the server is really an error - */ - if (pdsession.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { - /* If the Auth model is Basic, we probably got back a 404 Not found. - * But that's OK, because logout from Basic is meaningless on the - * server side unless it happens to be stateful, which is the only - * reason we even try calling j_spring_security_logout - */ - if (xhr.status === 404) { - logoutSucceeded = true; - } - else { - logoutSucceeded = false; - throw new Error("Error logging out, HTTP status = " + xhr.status); - } - } - else { - // for Form auth, any error on logout is an error - logoutSucceeded = false; - - // page refresh - we should call _reinitializeAfterLogout, or do something, so that - // caller can try logging in again (this is not a problem specific to page refresh, - // but the case of a page refresh after a server has gone down emphasizes it) - - throw new Error("Error logging out, HTTP status = " + xhr.status); - } - } - else { - logoutSucceeded = true; - } - - updateContextPropsFromResponse(pdsession, xhr); - pdsession._reinitializeAfterLogout(pdsession, logoutSucceeded); - }; - - this._reinitializeAfterLogout = function (pdsession, success) { - setLoginResult(null, pdsession); - setLoginHttpStatus(null, pdsession); - setClientContextID(null, pdsession); - setUserName(null, pdsession); - _password = null; - setAuthProvider(null); - - if (success) { - setRestApplicationIsOnline(false); - setOepingAvailable(false); - setPartialPingURI(defaultPartialPingURI); - setLastSessionXHR(null, pdsession); - clearTimeout(_timeoutID); // stop autopinging - } - }; - - - /* addCatalog - * - */ - this.addCatalog = function (arg1, arg2, arg3, arg4) { - var catalogURI, - catalogUserName, - catalogPassword, - isAsync = false, - xhr, - deferred, - iOSBasicAuthTimeout, - catalogIndex, - authProvider, - that = this; - - function addCatalogAfterOpen() { - /* This is here as much for CORS situations as the possibility that there might be an - * out of date cached version of the catalog. The CORS problem happens if you have - * accessed the catalog locally and then run an app on a different server that requests - * the catalog. Your browser already has the catalog, but the request used to get it was - * a non-CORS request and the browser will raise an error - */ - progress.data.Session._setNoCacheHeaders(xhr); - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(that, xhr); - - if (isAsync) { - xhr.onreadystatechange = that._onReadyStateChangeGeneric; - xhr.onResponseFn = that._processAddCatalogResult; - xhr.onResponseProcessedFn = that._addCatalogComplete; - - if (that.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC - && isUserAgentiOS - && iOSBasicAuthTimeout) { - xhr._requestTimeout = setTimeout(function () { - clearTimeout(xhr._requestTimeout); - xhr._iosTimeOutExpired = true; - xhr.abort(); - }, - iOSBasicAuthTimeout); - } - - // in case the caller is a JSDOSession - xhr._jsdosession = jsdosession; - xhr._deferred = deferred; - xhr._catalogIndex = catalogIndex; - } - - try { - if (typeof that.onOpenRequest === 'function') { - setLastSessionXHR(xhr, that); - var params = { - "xhr": xhr, - "verb": "GET", - "uri": catalogURI, - "async": false, - "formPreTest": false, - "session": that - }; - that.onOpenRequest(params); - xhr = params.xhr; - } - - setLastSessionXHR(xhr, that); - xhr.send(null); - } catch (e) { - throw new Error("Error retrieving catalog '" + catalogURI + "'.\n" + e.message); - } - if (isAsync) { - return progress.data.Session.ASYNC_PENDING; - } else { - return that._processAddCatalogResult(xhr); - } - - } - - if (this._isInvalidated) { - // JSDOSession: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); - } - - // Assume we're using a custom username/pw/authprovider - customCredentials = true; - - // check whether the args were passed in a single object. If so, copy them - // to the named arguments and a variable - if (arguments.length > 0) { - if (typeof arg1 === 'object') { - // check whether it's OK to add a catalog whilst offline - if (!arguments[0].offlineAddCatalog) { - if ((this.loginResult !== progress.data.Session.LOGIN_SUCCESS - && !this._authProvider) - && this.authenticationModel) { - throw new Error("Attempted to call addCatalog when there is no active session."); - } - } - - catalogURI = arg1.catalogURI; - if (!catalogURI || (typeof catalogURI !== 'string')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', - 'The catalogURI argument was missing or invalid.')); - } - catalogUserName = arg1.userName; - if (catalogUserName && (typeof catalogUserName !== 'string')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', - 'The catalogUserName argument was invalid.')); - } - catalogPassword = arg1.password; - if (catalogPassword && (typeof catalogPassword !== 'string')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', - 'The catalogPassword argument was invalid.')); - } - isAsync = arg1.async; - if (isAsync && (typeof isAsync !== 'boolean')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', - 'The async argument was invalid.')); - } - iOSBasicAuthTimeout = arg1.iOSBasicAuthTimeout; - if (typeof iOSBasicAuthTimeout === 'undefined') { - iOSBasicAuthTimeout = defaultiOSBasicAuthTimeout; - } else if (iOSBasicAuthTimeout && (typeof iOSBasicAuthTimeout !== 'number')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', - 'The iOSBasicAuthTimeout argument was invalid.')); - } - authProvider = arg1.authProvider; - - /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes - deferred, jsdosession, and catalogIndex and we need to eventually attach them to the - XHR we use so that the promise created by the JSDOSession will work correctly - */ - deferred = arg1.deferred; - catalogIndex = arg1.catalogIndex; - } else { - catalogURI = arg1; - if (typeof catalogURI !== 'string') { - throw new Error("First argument to Session.addCatalog must be the URL of the catalog."); - } - catalogUserName = arg2; - if (catalogUserName && (typeof catalogUserName !== 'string')) { - throw new Error("Second argument to Session.addCatalog must be a user name string."); - } - catalogPassword = arg3; - if (catalogPassword && (typeof catalogPassword !== 'string')) { - throw new Error("Third argument to Session.addCatalog must be a password string."); - } - } - } else { - throw new Error("Session.addCatalog is missing its first argument, the URL of the catalog."); - } - - if (!authProvider) { - authProvider = this._authProvider; - - // Guess we're using the default credentials passed earlier - customCredentials = false; - } - - // TODO: we expect that there will always be an authProvider if a login has been done. - // Therefore, we don't need to set catalogUsername and catalogPassword if they aren't - // passed in. What we should do here, when we extend the AuthenticationProvider API - // for the older auth models, is take any uname and pw passed in and create an auth - // provider, log in to the catalogURI with it, create an authImpl, and then fetch the - // catalog. - if (!catalogUserName) { - catalogUserName = this.userName; - } - - if (!catalogPassword) { - catalogPassword = _password; - } - - xhr = new XMLHttpRequest(); - xhr.pdsession = this; - xhr._catalogURI = catalogURI; - - // for now we don't support multiple version of the catalog across sessions - if (progress.data.ServicesManager.getSession(catalogURI) !== undefined) { - if (isAsync) { - /* - Attempt to get the event to fire AFTER this call returns ASYNC_PENDING - (and if the method was called from a JSDOSession, create an xhr to communicate - information related to promises back to its afterAddCatalog handler). Note that - the xhr is never used to make a request, it just carries data in the way - expected by the handler) - */ - // in case the caller is a JSDOSession - xhr._jsdosession = jsdosession; - xhr._deferred = deferred; - xhr._catalogIndex = catalogIndex; - - setTimeout(this._addCatalogComplete, 10, this, - progress.data.Session.CATALOG_ALREADY_LOADED, null, xhr); - return progress.data.Session.ASYNC_PENDING; - } - return progress.data.Session.CATALOG_ALREADY_LOADED; - } - - if (authProvider) { - authProvider._openRequestAndAuthorize(xhr, 'GET', catalogURI, isAsync, addCatalogAfterOpen); - // existing code in JSDOSession addCatalog expects to get this as a return value, - // have to return it now - return progress.data.Session.ASYNC_PENDING; - } else { // should be able to get rid of this if we do away with synchronous (old Session API) support - this._setXHRCredentials(xhr, 'GET', catalogURI, catalogUserName, catalogPassword, isAsync); - // Note that we are not adding the CCID to the URL or as a header, because the catalog may not - // be stored with the REST app and even if it is, the AppServer ID shouldn't be relevant - - return addCatalogAfterOpen(); - } - - }; - - this._processAddCatalogResult = function (xhr) { - var _catalogHttpStatus = xhr.status; - var theSession = xhr.pdsession; - var servicedata; - var catalogURI = xhr._catalogURI, - serviceURL, - theJSDOSession = jsdosession; - - // Only change the Session's state if the default AuthProv is being used - if (!customCredentials) { - toggleOnlineState(xhr); - } - - if ((_catalogHttpStatus == 200) || (_catalogHttpStatus === 0) && xhr.responseText) { - servicedata = theSession._parseCatalog(xhr); - try { - progress.data.ServicesManager.addCatalog(servicedata, theSession); - } - catch (e) { - if (progress.data.ServicesManager.getSession(catalogURI) !== undefined) { - /* this failed because the catalog had already been loaded, but the code - in addCatalog did not catch that, probably because we are executing - the JSDOSession addCatalog with multiple catalogURIs passed, and 2 - are the same - */ - return progress.data.Session.CATALOG_ALREADY_LOADED; - } - // different catalogs, with same resource name - throw new Error("Error processing catalog '" + catalogURI + "'. \n" + e.message); - } - // create a mobile service object and add it to the Session's array of same - for (var i = 0; i < servicedata.length; i++) { - serviceURL = theSession._prependAppURL(servicedata[i].address); - pushService(new progress.data.MobileServiceObject( - { - name: servicedata[i].name, - uri: serviceURL - }), - theSession); - - if (servicedata[i].settings - && servicedata[i].settings.useXClientProps - && !theSession.xClientProps) { - console.warn("Catalog warning: Service settings property 'useXClientProps' " + - "is true but 'xClientProps' property has not been set."); - } - } - pushCatalogURIs(catalogURI, theSession); - progress.data.ServicesManager.addSession(catalogURI, theSession); - if (theJSDOSession) { - progress.data.ServicesManager.addJSDOSession(catalogURI, theJSDOSession); - } - } - else if (_catalogHttpStatus == 401) { - return progress.data.AuthenticationProvider._getAuthFailureReason(xhr); - } - else if (xhr._iosTimeOutExpired) { - throw new Error( progress.data._getMsgText("jsdoMSG047", "addCatalog") ); - } - else { - throw new Error("Error retrieving catalog '" + catalogURI + - "'. Http status: " + _catalogHttpStatus + "."); - } - - return progress.data.Session.SUCCESS; - }; - - this._addCatalogComplete = function (pdsession, result, errObj, xhr) { - pdsession.trigger("afterAddCatalog", pdsession, result, errObj, xhr); - }; - - - /* - * ping -- determine whether the Mobile Web Application that the Session object represents - * is available, which includes determining whether its associated AppServer is running - * Also determine whether the Mobile services managed by this Session object are available - * (which means simply that they're known to the Mobile Web Application) - * (Implementation note: be sure that this Session object's "connected" - * property retains its current value until the end of this function, where - * it gets updated, if necessary, after calling _isOnlineStateChange - * - * Signatures : - * @param arg - * There are 2 signatures -- - * - no argument -- do an async ping of the Session's Mobile Web application. The only effect - * of the ping will be firing an offline or an online event, if appropriate - * The ping function itself will return false to the caller - * - object argument -- the object's properties provide the input args. They are all - * optional (if for some reason the caller passes an object that has no properties, it's - * the same as passing no argument at all). The properties may be: - * async -- tells whether to execute the ping asynchronously (which is the default) - * onCompleteFn -- if async, this will be called when response returns - * doNotFireEvent -- used internally, controls whether the ping method causes an offline - * or online event to be fired if there has been a change (the default is that it - * does, but our Session._checkServiceResponse() sets this to true so that it can - * control the firing of the event) - * offlineReason -- if present, and if the ping code discovers that teh server is offline, - * the ping code will set this with its best guess - * as to the reason the server is offline - */ - this.ping = function (args) { - var pingResult = false; - var pingArgs = { - pingURI: null, async: true, onCompleteFn: null, - fireEventIfOfflineChange: true, onReadyStateFn: this._onReadyStateChangePing, - offlineReason: null - }; - - if (this._isInvalidated) { - // Session: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "Session")); - } - - if ((!this._authProvider) && (this.loginResult !== progress.data.Session.LOGIN_SUCCESS)) { - throw new Error("Attempted to call ping when not logged in."); - } - - if (args) { - if (args.async !== undefined) { - // when we do background pinging (because pingInterval is set), - // we pass in an arg that is just an object that has an async property, - // set to true. This can be expanded to enable other kinds of ping calls - // to be done async (so that application developers can do so, if we decide - // to support that) - pingArgs.async = args.async; - } - - if (args.doNotFireEvent !== undefined) { - pingArgs.fireEventIfOfflineChange = !args.doNotFireEvent; - } - - if (args.onCompleteFn && (typeof args.onCompleteFn) == 'function') { - pingArgs.onCompleteFn = args.onCompleteFn; - } - /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes - deferred and jsdosession and we need to eventually attach them to the XHR we use so that - the promise created by the JSDOSession will work correctly - */ - pingArgs.deferred = args.deferred; - pingArgs.jsdosession = args.jsdosession; - - } - - - /* Ping the Mobile Web Application (this will also determine whether AppServer is available) - * Call _processPingResult() if we're synchronous, otherwise the handler for the xhr.send() - * will call it - */ - pingArgs.pingURI = that._makePingURI(); - that._sendPing(pingArgs); - if (!pingArgs.async) { - if (pingArgs.xhr) { - pingResult = that._processPingResult(pingArgs); - if (args.offlineReason !== undefined) { - args.offlineReason = pingArgs.offlineReason; - } - } - else { - pingResult = false; // no xhr returned from _sendPing, something must have gone wrong - } - if ( args.xhr !== undefined ) { - // if it's a sync ping, return the xhr if caller indicates they want it - // (there's almost guaranteed to be one, even if the ping was never sent - // if for some reason there isn't, we give them the null or undefined we ended up with) - args.xhr = pingArgs.xhr; - } - } - // else it's async, deliberately returning false - // so developer not misled into thinking the ping succeeded - - return pingResult; - }; - - - // "protected" Functions - - /* - * given a value of true or false for being online for the Mobile Web Application - * managed by this Session object, determine whether that changes the current - * state of being offline or online. - * Returns true if the input state is a change from the current state - * - * Signature : - * @param isOnline Required. True to determine whether online is a state change, false to - * determine whether offline constitutes a state change. Boolean. - * - */ - this._isOnlineStateChange = function (isOnline) { - var stateChanged = false; - - if (isOnline && !(this.connected)) { - stateChanged = true; - } - else if (!isOnline && ( this.connected )) { - stateChanged = true; - } - - return stateChanged; - }; - - - /* - * given information about the response from a request made to a service, - * do the following: - * - * determine whether the online status of the Session has changed, and - * set the Session's Connected property accordingly - * if the Session's online status has changed, fire the appropriate event - * - * Signature : - * @param xhr Required. The xhr that was used to make the request. Object - * @param success Required. True if caller regards the request as having succeeded. Boolean - * @param request Required. The JSDO request object created for making the request. Object. - * - */ - this._checkServiceResponse = function (xhr, success, request) { - var offlineReason = null, - wasOnline = this.connected; - updateContextPropsFromResponse(this, xhr); - - /* first of all, if there are no subscriptions to offline or online events, don't - * bother -- we don't want to run the risk of messing things up by calling ping - * if the app developer isn't interested (especially because that may mean that - * ping isn't enabled on the server, anyway) - */ - if (!this._events) { - return; - } - var offlineObservers = this._events["offline"] || []; - var onlineObservers = this._events["online"] || []; - if ((offlineObservers.length === 0) && (onlineObservers.length === 0)) { - return; - } - - /* even though this function gets called as a result of trying to - * contact the server, don't bother to change anything if we already - * know that the device (or user agent, or client machine) is offline. - * We can't assume anything about the state of the server if we can't - * even get to the internet from the client - */ - - // if the call to the server was a success, we will assume we are online, - // both server and device - if (success) { - setRestApplicationIsOnline(true); - setDeviceIsOnline(true); // presumably this is true (probably was already true) - } - else { - /* Request failed, determine whether it's because server is offline - * Do this even if the Session was already in an offline state, because - * we need to determine whether the failure was due to still being - * offline, or whether it's now possible to communicate with the - * server but the problem was something else. - */ - - if (deviceIsOnline) { - /* ping the server to get better information on whether this is an offline case - * NB: synchronous ping for simplicity, maybe should consider async so as not - * to potentially freeze UI - */ - var localPingArgs = { - doNotFireEvent: true, // do in this fn so we have the request - offlineReason: null, - async: false - }; - if (!(that.ping(localPingArgs) )) { - offlineReason = localPingArgs.offlineReason; - setRestApplicationIsOnline(false); - } - else { - // ping returned true, so even though the original request failed, - // we are online and the failure must have been due to something else - setRestApplicationIsOnline(true); - } - } - // else deviceIsOnline was already false, so the offline event should already have - // been fired for that reason and there is no need to do anything else - } - - if (wasOnline && !this.connected) { - this.trigger("offline", this, offlineReason, request); - } - else if (!wasOnline && this.connected) { - this.trigger("online", this, request); - } - }; - - /* Decide whether, on the basis of information returned by a server request, the - * Mobile Web Application managed by this Session object is online, where online - * means that the ping response was a 200 and, IF the body of the response contains - * JSON with an AppServerStatus property, that AppServerStatus Status property has - * a pingStatus property set to true - * i.e., the body has an AppServerStatus.PingStatus set to true - * (if the body doesn't contain JSON with an AppServerStatus, we use just the HTTP - * response status code to decide) - * - * Returns: true if the response meets the above conditions, false if it doesn't - * - * Parameters: - * args, with properties: - * xhr - the XMLHttpRequest used to make the request - * offlineReason - if the function determines that the app is offline, - * it sets offlineReason to the reason for that decision, - * for the use of the caller - * fireEventIfOfflineChange - if true, the function fires an offline or online - * event if there has been a change (i.e., the online state determined - * by the function is different from what it had been when the function - * began executing) - * usingOepingFormat - OPTIONAL. The function's default assumption is that the value - * of the session's internal oepingAvailable variable indicates whether the - * the response body will be in the format used by the OpenEdge oeping service. - * A caller can override this assumption by using this property to true or false. - * (the isAuthorized code sets this to false because it doesn't use oeping - * but does call this function) - */ - this._processPingResult = function (args) { - var xhr = args.xhr, - pingResponseJSON, - appServerStatus = null, - wasOnline = this.connected, - connectedBeforeCallback, - assumeOepingFormat; - - if (args.hasOwnProperty('usingOepingFormat')) { - assumeOepingFormat = args.usingOepingFormat; - } else { - assumeOepingFormat = oepingAvailable; - } - - /* first determine whether the Web server and the Mobile Web Application (MWA) - * are available - */ - if (xhr.status >= 200 && xhr.status < 300) { - updateContextPropsFromResponse(this, xhr); - if (assumeOepingFormat) { - try { - pingResponseJSON = JSON.parse(xhr.responseText); - appServerStatus = pingResponseJSON.AppServerStatus; - } - catch (e) { - /* We got a successful response from calling our ping URI, but it - * didn't return valid JSON. If we think that the oeping REST API - * is available on the server (so we should have gotten valid - * json), log this to the console. - * - */ - console.error("Unable to parse ping response."); - } - } - toggleOnlineState(xhr); - } - else { - if (deviceIsOnline) { - if (xhr.status === 0) { - args.offlineReason = progress.data.Session.SERVER_OFFLINE; - setRestApplicationIsOnline(false); - } - else if ((xhr.status === 404) || (xhr.status === 410)) { - /* if we get a 404, it means the Web server is up, but it - * can't find the resource we requested (either _oeping or - * the login target), therefore the Mobile Web application - * must be unavailable (410 is Gone) - */ - args.offlineReason = progress.data.Session.WEB_APPLICATION_OFFLINE; - setRestApplicationIsOnline(false); - } - else { - /* There's some error, but we can't say for sure that it's because - * the Web application is unavailable. May be an authentication problem, - * internal server error, or for some reason our ping request was - * invalid (unlikely to happen if it previously succeeded). - * In particular, if the server uses Form authentication, it - * may have come back online but now the session id - * is no longer valid. - */ - setRestApplicationIsOnline(true); - } - } - else { - args.offlineReason = progress.data.Session.DEVICE_OFFLINE; - } - } - - // is the AppServer online? appServerStatus will be non-null only - // if the ping request returned 200, meaning the other things are OK - // (connection to server, Tomcat, Mobile Web application) - if (appServerStatus) { - if (appServerStatus.PingStatus === "false") { - args.offlineReason = progress.data.Session.APPSERVER_OFFLINE; - setRestApplicationIsOnline(false); - } - else { - setRestApplicationIsOnline(true); - } - } - - /* We call any async ping callback handler and then, after that returns, fire an - offline or online event if necessary. - When deciding whether to fire an event, the responsibility of this _processPingResult() - function is to decide about the event on the basis of the data returned from the ping - that it is currently processing. Therefore, since the ping callback that is just about - to be called could change the outcome of the event decision (for example, if the handler - calls logout(), thus setting Session.connected to false)), we save the current value of - Session.connected and use that saved value to decide about the event after the ping - handler returns. - (If the application programmer wants to get an event fired as a result of something - that happens in the ping handler, they should call a ping() *after* that. - */ - connectedBeforeCallback = this.connected; - - if ((typeof xhr.onCompleteFn) == 'function') { - xhr.onCompleteFn({ - pingResult: this.connected, - xhr: xhr, - offlineReason: args.offlineReason - }); - } - - // decide whether to fire an event, and if so do it - if (args.fireEventIfOfflineChange) { - if (wasOnline && !connectedBeforeCallback) { - that.trigger("offline", that, args.offlineReason, null); - } - else if (!wasOnline && connectedBeforeCallback) { - that.trigger("online", that, null); - } - } - - return this.connected; - }; - - - this._onReadyStateChangePing = function () { - var xhr = this; - var args; - - if (xhr.readyState == 4) { - args = { - xhr: xhr, - fireEventIfOfflineChange: true, - offlineReason: null - }; - that._processPingResult(args); - if (_pingInterval > 0) { - _timeoutID = setTimeout(that._autoping, _pingInterval); - } - } - }; - - this._pingtestOnReadyStateChange = function () { - var xhr = this; - - if (xhr.readyState == 4) { - var foundOeping = false; - if (xhr.status >= 200 && xhr.status < 300) { - foundOeping = true; - } - else { - setPartialPingURI(that.loginTarget); - console.warn("Default ping target not available, will use loginTarget instead."); - } - setOepingAvailable(foundOeping); - - // If we're here, we've just logged in. If pingInterval has been set, we need - // to start autopinging - if (_pingInterval > 0) { - _timeoutID = setTimeout(that._autoping, _pingInterval); - } - } - }; - - /* - * args: pingURI - * async - * onCompleteFn used only if async is true - * - * (deliberately not catching thrown error) - */ - this._sendPing = function (args) { - var xhr = new XMLHttpRequest(), - that = this; - - function sendPingAfterOpen() { - if (args.async) { - xhr.onreadystatechange = args.onReadyStateFn; - xhr.onCompleteFn = args.onCompleteFn; - xhr._jsdosession = jsdosession; // in case the Session is part of a JSDOSession - xhr._deferred = args.deferred; // in case the Session is part of a JSDOSession - } - progress.data.Session._setNoCacheHeaders(xhr); - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(that, xhr); - if (that.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - _addWithCredentialsAndAccept(xhr, - "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - } - xhr.send(null); - } - - try { - if (this._authProvider) { - this._authProvider._openRequestAndAuthorize(xhr, - 'GET', - args.pingURI, - args.async, - sendPingAfterOpen); - } else { - // get rid of this if we do away with synchronous support (i.e., customer use of - // old Session API) - this._setXHRCredentials(xhr, "GET", args.pingURI, this.userName, _password, args.async); - - // Sending the XHR request after opening the channel - if (xhr.readyState === 1) { - sendPingAfterOpen(); - } - } - } catch (e) { - args.error = e; - } - - args.xhr = xhr; - }; - - this._makePingURI = function () { - var pingURI = this.serviceURI + partialPingURI; - // had caching problem with Firefox in its offline mode - if (progress.data.Session._useTimeStamp) { - pingURI = progress.data.Session._addTimeStampToURL(pingURI); - } - return pingURI; - }; - - - /* - * autoping -- callback - */ - this._autoping = function () { - that.ping({async: true}); - }; - - - // TODO for API revamp: get rid of this method and replace it with implementations - // of AUthenticationImplementation.openRequest that are specific to the - // auth models (assuming we can use some sort of subclassing or interface design) - // (and when we remove this, remove the calls to it in this file) - /* _setXHRCredentials (intended for progress.data library use only) - * set credentials as needed, both via the xhr's open method and setting the - * Authorization header directly - */ - this._setXHRCredentials = function (xhr, verb, uri, userName, password, async) { - - // note that we do not set credentials if userName is null. - // Null userName indicates that the developer is depending on the browser to - // get and manage the credentials, and we need to make sure we don't interfere with that - if (userName - && this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { - - // See the comment at the definition of the canPassCredentialsToOpen() function - // for why we pass credentials to open() in some cases but not others. (If we're not using - // Basic auth, we never pass credentials) - if (canPassCredentialsToOpen()) { - xhr.open(verb, uri, async, userName, password); - } - else { - xhr.open(verb, uri, async); - } - - // set Authorization header - var auth = _make_basic_auth(userName, password); - xhr.setRequestHeader('Authorization', auth); - } - else { - xhr.open(verb, uri, async); - } - }; - - /* _addCCIDtoURL (intended for progress.data library use only) - * Add the Client Context ID being used by a session on an OE REST application, if we have - * previously stored one from a response from the server - */ - this._addCCIDtoURL = function (url) { - var urlPart1, - urlPart2, - jsessionidStr, - index; - - if (this.clientContextId && (this.clientContextId !== "0")) { - // Should we test protocol, - // host and port in addition to path to ensure that jsessionid is only sent - // when request applies to the REST app (it might not be if the catalog is somewhere else) - if (url.substring(0, this.serviceURI.length) == this.serviceURI) { - jsessionidStr = ";" + "JSESSIONID=" + this.clientContextId; - index = url.indexOf('?'); - if (index == -1) { - url += jsessionidStr; // just append the jsessionid path parameter to the path - } - else { - // insert jsessionid path parameter before the first query parameter - urlPart1 = url.substring(0, index); - urlPart2 = url.substring(index); - url = urlPart1 + jsessionidStr + urlPart2; - } - } - } - return url; - }; - - /* _saveClientContextId (intended for progress.data library use only) - * If the CCID hasn't been set for the session yet, check the xhr for it and store it. - * (If it has been set, assume that the existing one is correct and do nothing. We could - * enhance this function by checking to see whether the new one matches the existing one. - * Not sure what to do if that's the case -- overwrite the old one? ignore the new one? - * Should at least log a warning or error - */ - this._saveClientContextId = function (xhr) { - // do this unconditionally (even if there is already a client-context-id), because - // if basic authentication is set up such that it uses sessions, and cookies are disabled, - // the server will generate a different session on each request and the X-CLIENT-CONTEXT-ID - // will therefore be different - setClientContextIDfromXHR(xhr, this); - }; - - this._parseCatalog = function (xhr) { - var jsonObject; - var catalogdata; - - try { - jsonObject = JSON.parse(xhr.responseText); - catalogdata = jsonObject.services; - } - catch (e) { - console.error("Unable to parse response. Make sure catalog has correct format."); - catalogdata = null; - } - - return catalogdata; - }; - - /* _prependAppURL - * Prepends the URL of the Web application - * (the 1st parameter passed to login, stored in this.serviceURI) - * to whatever string is passed in. If the string passed in is an absolute URL, this function does - * nothing except return a copy. This function ensures that the resulting URL has the correct number - * of slashes between the web app url and the string passed in (currently that means that if what's - * passed in has no initial slash, the function adds one) - */ - this._prependAppURL = function (oldURL) { - if (!oldURL) { - /* If oldURL is null, just return the app URL. (It's not the responsibility of this - * function to decide whether having a null URL is an error. Its only responsibility - * is to prepend the App URL to whatever it gets passed - * (and make sure the result is a valid URL) - */ - return this.serviceURI; - } - var newURL = oldURL; - var pat = /^https?:\/\//i; - if (!pat.test(newURL)) { - if (newURL.indexOf("/") !== 0) { - newURL = "/" + newURL; - } - - newURL = this.serviceURI + newURL; - } - return newURL; - }; - - - // Functions - - // get rid of this if we get rid of synchronous (old Session object API) support? - // Set an XMLHttpRequest object's withCredentials attribute and Accept header, - // using a try-catch so that if setting withCredentials throws an error it doesn't - // interrupt execution (this is a workaround for the fact that Firefox doesn't - // allow you to set withCredentials when you're doing a synchronous operation) - // The setting of the Accept header is included here, and happens after the - // attempt to set withCredentials, to make the behavior in 11.3.0 match - // the behavior in 11.2.1 -- for Firefox, in a CORS situation, login() will - // fail. (If we allowed the Accept header to be set, login() would succeed - // because of that but addCatalog() would fail because no JSESSIONID would - // be sent due to withCredentials not being true) - function _addWithCredentialsAndAccept(xhr, acceptString) { - try { - xhr.withCredentials = true; - xhr.setRequestHeader("Accept", acceptString); - } - catch (e) { - } - } - - // get rid of this if we get rid of synchronous (old Session API) support? - // (because it's in AuthenticationProviderBasic) - // from http://coderseye.com/2007/how-to-do-http-basic-auth-in-ajax.html - function _make_basic_auth(user, pw) { - var tok = user + ':' + pw; - var hash = btoa(tok); - return "Basic " + hash; - } - - /* The next 2 functions, _gotLoginForm() and _gotLoginFailure(), attempt to determine whether - * a server response consists of - * the application's login page or login failure page. Currently (release 11.2), this - * is the only way we have of determining that a request made to the server that's - * configured for form-based authentication failed due to authentication (i.e., - * authentication hadn't happened before the request and either invalid credentials or - * no credentials were sent to the server). That's because, due to the fact that the browser - * or native wrapper typically intercepts the redirect involved in an unauthenticated request - * to a server that's using using form auth, all we see in the XHR is a success status code - * plus whatever page we were redirected to. - * In the future, we expect to enhance the OE REST adapter so that it will return a status code - * indicating failure for form-based authentication, and we can reimplement these functions so - * they check for that code rather than do the simplistic string search. - */ - - // Determines whether the content of the xhr is the login page. Assumes - // use of a convention for testing for login page - var loginFormIDString = "j_spring_security_check"; - - function _gotLoginForm(xhr) { - // is the response contained in an xhr actually the login page? - return _findStringInResponseHTML(xhr, loginFormIDString); - } - - // Determines whether the content of the xhr is the login failure page. Assumes - // use of a convention for testing for login fail page - var loginFailureIdentificationString = "login failed"; - - function _gotLoginFailure(xhr) { - return _findStringInResponseHTML(xhr, loginFailureIdentificationString); - } - - // Does a given xhr contain html and does that html contain a given string? - function _findStringInResponseHTML(xhr, searchString) { - if (!xhr.responseText) { - return false; - } - var contentType = xhr.getResponseHeader("Content-Type"); - - if ((contentType.indexOf("text/html") >= 0) && - (xhr.responseText.indexOf(searchString) >= 0)) { - return true; - } - - return false; - } - - // get rid of this if we get rid of synchronous (old Session API) support? - /* sets the statusFromjson property in the params object to indicate - * the status of a response from an OE Mobile Web application that has - * to do with authentication (the response to a login request, or a - * response to a request for a resource where there was an error having - * to do with authentication */ - function handleJSONLoginResponse(params) { - // Parse the json in the response to see whether it's the special OE REST service - // response. If it is, check the result (which should be consistent with the status from - // the xhr) - var jsonObject; - params.statusFromjson = null; - try { - jsonObject = JSON.parse(params.xhr.responseText); - - if (jsonObject.status_code !== undefined - && jsonObject.status_txt !== undefined) { - params.statusFromjson = jsonObject.status_code; - } - } - catch (e) { - // invalid json - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, params.session); - setLoginHttpStatus(xhr.status, params.session); - throw new Error("Unable to parse login response from server."); - } - - } - - function setRequestHeaderFromContextProps(session, xhr) { - if (session.xClientProps) { - xhr.setRequestHeader("X-CLIENT-PROPS", session.xClientProps); - } - else if (session._contextProperties.contextHeader !== undefined) { - xhr.setRequestHeader("X-CLIENT-PROPS", session._contextProperties.contextHeader); - } - } - - function toggleOnlineState(xhr) { - var pdsession = that; - - setLoginHttpStatus(xhr.status, pdsession); - - if (pdsession.loginHttpStatus >= 200 && pdsession.loginHttpStatus < 400) { - setLoginResult(progress.data.Session.LOGIN_SUCCESS, pdsession); - setRestApplicationIsOnline(true); - pdsession._saveClientContextId(xhr); - storeAllSessionInfo(); // save info to persistent storage - } else { - // Taking a page from _processPingResult where we set the rest application as offline if it's one of - // these error codes - if (pdsession.loginHttpStatus === 0 || pdsession.loginHttpStatus === 400 || pdsession.loginHttpStatus === 410) { - setRestApplicationIsOnline(false); - setLoginResult(progress.data.AuthenticationProvider._getAuthFailureReason(xhr), - pdsession); - } - // Otherwise if it's probably an internal error or auth problem. Either way, we know it's still online. - else { - - setRestApplicationIsOnline(true); - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, pdsession); - } - - - } - - setLastSessionXHR(xhr, pdsession); - updateContextPropsFromResponse(pdsession, xhr); - - return pdsession.loginResult; - }; - - function updateContextPropsFromResponse(session, xhr) { - /* determine whether the response contains an X-CLIENT_PROPS header and, if so, - set the Session's context - */ - var contextString, - context; - - if (xhr) { - contextString = getResponseHeaderNoError(xhr, "X-CLIENT-PROPS"); - if (contextString) { - try { - context = JSON.parse( contextString ); - } - catch(e) { - } - if (typeof context === "object") { - session._contextProperties.setContext( context ); - } - else { - //{1}: A server response included an invalid {2} header. - throw new Error(progress.data._getMsgText("jsdoMSG123", 'Session', 'X-CLIENT-PROPS')); - } - } - else if (contextString === "") { - // If header is "", clear the X-CLIENT-PROPS context, - session._contextProperties.setContext( {} ); - } - // if header is absent (getResponseHeader will return null), don't change _contextProperties - } - } - - // Remove all resources, services, and sessions related to this Session from the ServicesManager - function cleanServicesManager() { - progress.data.ServicesManager.cleanSession(that); - } - - // process constructor options and do other initialization - - // If a storage key (name property of a JSDOSession) was passed to the constructor, - // use it to try to retrieve state data from a previous JSDOSession instance that - // had the same name. This code was introduced to handle page refreshes, but could - // be used for other purposes. - if (typeof (options) === 'object') { - - jsdosession = options.jsdosession; - newURI = options.serviceURI; - setAuthProvider(options.authProvider); // do this BEFORE calling setSessionInfoFromStorage - - if (options.authProvider && options.authProvider.hasClientCredentials()) { - _loginResult = progress.data.Session.LOGIN_SUCCESS; - } - - // get rid of trailing '/' because appending service url that starts with '/' - // will cause request failures - if (newURI && newURI[newURI.length - 1] === "/") { - newURI = newURI.substring(0, newURI.length - 1); - } - - _storageKey = options._storageKey; - if (_storageKey) { - if (retrieveSessionInfo(_storageKey)) { - storedAuthModel = retrieveSessionInfo("authenticationModel"); - storedURI = retrieveSessionInfo("serviceURI"); - - if ((storedAuthModel !== options.authenticationModel) || - (storedURI !== newURI)) { - clearAllSessionInfo(); - } else { - // Note: be sure we have set authProvider (if any) from options before - // calling setSessionInfoFromStorage (important so that the logic in - // setSessionInfoFromStorage that re-creates an AuthenticationProvider - // after page refresh only gets used if the app is using the old JSDOSession.login) - setSessionInfoFromStorage(_storageKey); - stateWasReadFromStorage = true; - } - } - // _storageKey is in essence the flag for page refresh; we are not supporting page refresh for Basic - // auth, so clear it even if it was passed in. - // (But had to set and keep _storageKey until this point so that the above validation of - // serviceURI and auth model will be done even in the case where there's a mismatch and - // the new auth model is Basic. This statement will go away when we support page refresh with - // Basic) - if (options.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { - _storageKey = undefined; - } - } - - // If we didn't read state info from storage, we need to set the serviceURI and probably - // the authenticationModel - if (!stateWasReadFromStorage) { - if (newURI) { - setServiceURI(newURI, this); - } - if (options.authenticationModel) { - this.authenticationModel = options.authenticationModel; - } - } - } - - }; // End of Session - progress.data.Session._useTimeStamp = true; - - var SEQ_MAX_VALUE = 999999999999999; - // 15 - 9 - var _tsseq = SEQ_MAX_VALUE; - // Initialized to SEQ_MAX_VALUE to initialize values. - var _tsprefix1 = 0; - var _tsprefix2 = 0; - - // this._getNextTimeStamp = function () { - progress.data.Session._getNextTimeStamp = function () { - var seq = ++_tsseq; - if (seq >= SEQ_MAX_VALUE) { - _tsseq = seq = 1; - var t = Math.floor(( Date.now ? Date.now() : (new Date().getTime())) / 10000); - if (_tsprefix1 == t) { - _tsprefix2++; - if (_tsprefix2 >= SEQ_MAX_VALUE) { - _tsprefix2 = 1; - } - } - else { - _tsprefix1 = t; - Math.random(); // Ignore call to random - _tsprefix2 = Math.round(Math.random() * 10000000000); - } - } - - return _tsprefix1 + "-" + _tsprefix2 + "-" + seq; - }; - - /* - * _addTimeStampToURL (intended for progress.data library use only) - * Add a time stamp to the a URL to prevent caching of the request. - * Set progress.data.Session._useTimeStamp = false to turn off. - */ - progress.data.Session._addTimeStampToURL = function (url) { - var timeStamp = "_ts=" + progress.data.Session._getNextTimeStamp(); - url += ((url.indexOf('?') == -1) ? "?" : "&") + timeStamp; - return url; - }; - - // Do whatever it takes to direct the XMLHttpRequest not to fulfill the request - // from a cache - // (convenience method --- we do this several different places in the code) - progress.data.Session._setNoCacheHeaders = function (xhr) { - xhr.setRequestHeader("Cache-Control", "no-cache"); - xhr.setRequestHeader("Pragma", "no-cache"); - }; - - - -// Constants for progress.data.Session - if ((typeof Object.defineProperty) == 'function') { - Object.defineProperty(progress.data.Session, 'LOGIN_AUTHENTICATION_REQUIRED', { - value: 0, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'LOGIN_SUCCESS', { - value: 1, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'LOGIN_AUTHENTICATION_FAILURE', { - value: 2, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'LOGIN_GENERAL_FAILURE', { - value: 3, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'CATALOG_ALREADY_LOADED', { - value: 4, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'ASYNC_PENDING', { - value: 5, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'EXPIRED_TOKEN', { - value: 6, enumerable: true - }); - - Object.defineProperty(progress.data.Session, 'SUCCESS', { - value: 1, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'AUTHENTICATION_FAILURE', { - value: 2, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'GENERAL_FAILURE', { - value: 3, enumerable: true - }); - - Object.defineProperty(progress.data.Session, 'AUTH_TYPE_ANON', { - value: "anonymous", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'AUTH_TYPE_BASIC', { - value: "basic", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'AUTH_TYPE_FORM', { - value: "form", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'AUTH_TYPE_SSO', { - value: "sso", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'AUTH_TYPE_FORM_SSO', { - value: "form_sso", enumerable: true - }); - - - Object.defineProperty(progress.data.Session, 'DEVICE_OFFLINE', { - value: "Device is offline", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'SERVER_OFFLINE', { - value: "Cannot contact server", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'WEB_APPLICATION_OFFLINE', { - value: "Mobile Web Application is not available", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'SERVICE_OFFLINE', { - value: "REST web Service is not available", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'APPSERVER_OFFLINE', { - value: "AppServer is not available", enumerable: true - }); - } - else { - progress.data.Session.LOGIN_SUCCESS = 1; - progress.data.Session.LOGIN_AUTHENTICATION_FAILURE = 2; - progress.data.Session.LOGIN_GENERAL_FAILURE = 3; - progress.data.Session.CATALOG_ALREADY_LOADED = 4; - - progress.data.Session.SUCCESS = 1; - progress.data.Session.AUTHENTICATION_FAILURE = 2; - progress.data.Session.GENERAL_FAILURE = 3; - - progress.data.Session.AUTH_TYPE_ANON = "anonymous"; - progress.data.Session.AUTH_TYPE_BASIC = "basic"; - progress.data.Session.AUTH_TYPE_FORM = "form"; - progress.data.Session.AUTH_TYPE_SSO = "sso"; - - /* deliberately not including the "offline reasons" that are defined in the - * 1st part of the conditional. We believe that we can be used only in environments where - * ECMAScript 5 is supported, so let's put that assumption to the test - */ - } - -//setup inheritance for Session -- specifically for incorporating an Observable object - progress.data.Session.prototype = new progress.util.Observable(); - progress.data.Session.prototype.constructor = progress.data.Session; - function validateSessionSubscribe(args, evt, listenerData) { - listenerData.operation = undefined; - var found = false; - - // make sure this event is one that we support - for (var i = 0; i < this._eventNames.length; i++) { - if (evt === this._eventNames[i].toLowerCase()) { - found = true; - break; - } - } - if (!found) { - throw new Error(progress.data._getMsgText("jsdoMSG042", evt)); - } - - if (args.length < 2) { - throw new Error(progress.data._getMsgText("jsdoMSG038", 2)); - } - - if (typeof args[0] !== 'string') { - throw new Error(progress.data._getMsgText("jsdoMSG039")); - } - - if (typeof args[1] !== 'function') { - throw new Error(progress.data._getMsgText("jsdoMSG040")); - } - else { - listenerData.fn = args[1]; - } - - if (args.length > 2) { - if (typeof args[2] !== 'object') { - throw new Error(progress.data._getMsgText("jsdoMSG041", evt)); - } - else { - listenerData.scope = args[2]; - } - } - } - // events supported by Session - progress.data.Session.prototype._eventNames = - ["offline", "online", "afterLogin", "afterAddCatalog", "afterLogout", "afterDisconnect"]; - // callback to validate subscribe and unsubscribe - progress.data.Session.prototype.validateSubscribe = validateSessionSubscribe; - progress.data.Session.prototype.toString = function (radix) { - return "progress.data.Session"; - }; - - - /* - progress.data.JSDOSession - Like progress.data.Session, but the methods are async-only and return promises. - (first implementation uses progress.data.Session to do the work, but conceivably - that implementation could be changed to something different) - The JSDOSession object keeps the same underlying pdsession object for the lifetime - of the JSDOSession object -- i.e., even after logout and subsequent login, the pdsession - is re-used rather than re-created. - */ - progress.data.JSDOSession = function JSDOSession(options) { - var _pdsession, - _serviceURI, - that = this, - _name; - - // PROPERTIES - // Approach: Use the properties of the underlying progress.data.Session object whenever - // possible. - Object.defineProperty(this, 'authenticationModel', - { - get: function () { - return _pdsession ? _pdsession.authenticationModel : undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'authProvider', - { - get: function () { - return _pdsession ? _pdsession._authProvider : null; - }, - enumerable: true - }); - Object.defineProperty(this, 'catalogURIs', - { - get: function () { - return _pdsession ? _pdsession.catalogURIs: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'clientContextId', - { - get: function () { - return _pdsession ? _pdsession.clientContextId: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'connected', - { - get: function () { - return _pdsession ? _pdsession.connected: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'JSDOs', - { - get: function () { - return _pdsession ? _pdsession.JSDOs: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'loginResult', - { - get: function () { - return _pdsession ? _pdsession.loginResult: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'loginHttpStatus', - { - get: function () { - return _pdsession ? _pdsession.loginHttpStatus: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'onOpenRequest', - { - get: function () { - return _pdsession ? _pdsession.onOpenRequest: undefined; - }, - set: function (newval) { - if (_pdsession) { - _pdsession.onOpenRequest = newval; - } - }, - enumerable: true - }); - - Object.defineProperty(this, 'pingInterval', - { - get: function () { - return _pdsession ? _pdsession.pingInterval: undefined; - }, - set: function (newval) { - if (_pdsession) { - _pdsession.pingInterval = newval; - } - }, - enumerable: true - }); - - Object.defineProperty(this, 'services', - { - get: function () { - return _pdsession ? _pdsession.services: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'serviceURI', - { - get: function () { - if (_pdsession && _pdsession.serviceURI) { - return _pdsession.serviceURI; - } - else { - return _serviceURI; - } - }, - enumerable: true - }); - - Object.defineProperty(this, 'userName', - { - get: function () { - return _pdsession ? _pdsession.userName: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'name', - { - get: function () { - return _name; - }, - enumerable: true - }); - - Object.defineProperty( - this, - "_isInvalidated", - { - get: function () { - return _pdsession._isInvalidated; - }, - enumerable: false - } - ); - - // PRIVATE FUNCTIONS - - - // Wrapper to make it easier to change the promise implementation we use. - // Note that in the JSDO library's first implementation of promise support, - // the "promise" parameter for this function is actually a jQuery Deferred object - function settlePromise(promise, fulfill, result, info) { - if (fulfill) { - promise.resolve(that, result, info); - } else { - promise.reject(that, result, info); - } - } - - // use this for the events fired by progress.data.Session that can be handled with common code - function genericSessionEventHandler(pdsession, result, errorObject, xhr, deferred) { - var myDeferred; - - if (xhr) { - myDeferred = xhr._deferred; - } else { - myDeferred = deferred; - } - - settlePromise(myDeferred, - result === progress.data.Session.SUCCESS ? true : false, - result, - { errorObject: errorObject, - xhr: xhr }); - } - - function onAfterAddCatalog( pdsession, result, errorObject, xhr ) { - var deferred, - fulfill = false, - settleResult; - - if (result === progress.data.Session.EXPIRED_TOKEN) { - settleResult = progress.data.Session.EXPIRED_TOKEN; - } else { - settleResult = progress.data.Session.GENERAL_FAILURE; - } - - if (xhr && xhr._deferred) { - deferred = xhr._deferred; - - /* add the result for this addCatalog to the result array. */ - if ( result !== progress.data.Session.SUCCESS && - result !== progress.data.Session.CATALOG_ALREADY_LOADED ) { - - result = result || progress.data.Session.GENERAL_FAILURE; - - /* Set a property on the deferred to indicates that the "overall" result was - a failure. When we decide whether to reject or resolve the promise, we reject - if it's set to GENERAL_FAILURE, otherwise we resolve the promise - (really only need to set this once, but simpler code if we just set (or possibly - re-set) it whenever we find an error, plus if, at some point while we're still - processing, it's important to know whether we've already had an error, we can - check the property) - */ - deferred._overallCatalogResult = progress.data.Session.GENERAL_FAILURE; - } - - deferred._results[xhr._catalogIndex] = { catalogURI : xhr._catalogURI, - result : result, - errorObject : errorObject, - xhr : xhr}; - deferred._numCatalogsProcessed += 1; - if ( deferred._numCatalogsProcessed === deferred._numCatalogs ) { - deferred._processedPromise = true; - - if ( !deferred._overallCatalogResult ) { - fulfill = true; - settleResult = progress.data.Session.SUCCESS; - } - settlePromise(xhr._deferred, - fulfill, - settleResult, - xhr._deferred._results); - } - } - } - - function onAfterLogout(pdsession, errorObject, xhr) { - var result = progress.data.Session.GENERAL_FAILURE, - fulfill = false; - if (xhr && xhr._deferred) { - /* Note: loginResult gets cleared on successful logout, so testing it for false - to confirm that logout succeeded - */ - if (!errorObject && !pdsession.loginResult) { - result = progress.data.Session.SUCCESS; - fulfill = true; - } - settlePromise(xhr._deferred, - fulfill, - result, - { errorObject: errorObject, - xhr: xhr }); - } - } - - function onPingComplete(args) { - var xhr = args.xhr; - if (xhr && xhr._deferred) { - settlePromise(xhr._deferred, - args.pingResult, // this tells settlePromise whether to resolve or reject - args.pingResult, // this is the result value passed to the promise handler - { offlineReason: args.offlineReason, - xhr: xhr }); - } - } - - // METHODS - - // login() - // Creates an AuthenticationProvider and calls its login() method. Any errors thrown by the - // Auth Provider's constructor or login will bubble up to the caller, otherwise this method - // returns the promise from the A-P's login call. - this.login = function (username, password, options) { - var deferred = $.Deferred(), - iOSBasicAuthTimeout; - - function callIsAuthorized() { - that.isAuthorized() - .then(function (jsdosession, result, info) { - deferred.resolve(that, result, info); - }, function (jsdosession, result, info) { - deferred.reject(that, result, info); - }); - } - - if (this._isInvalidated) { - // JSDOSession: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); - } - - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // JSDOSession: Cannot call login() when authenticationModel is SSO. - // Please use the AuthenticationProvider object instead. - throw new Error(progress.data._getMsgText("jsdoMSG057", - 'JSDOSession', - 'login()')); - } - - if (typeof options === 'object') { - iOSBasicAuthTimeout = options.iOSBasicAuthTimeout; - } - - if (!_pdsession._authProvider) { - // is there a better way to do this? Need it because we didn't have the authprovider when - // running the constructor - _pdsession._authProvider = new progress.data.AuthenticationProvider({ - uri: this.serviceURI, - authenticationModel: this.authenticationModel - }); - } - - - _pdsession._authProvider.logout() - .then( function () { - return _pdsession._authProvider.login(username, password); - }) - .then(function () { - callIsAuthorized(); - }, function (provider, result, info) { - deferred.reject(that, result, info); - }); - - return deferred.promise(); - }; - - // This method terminates the JSDOSession's ability to send requests to its serviceURI. - // Remove the reference to the AuthenticationProvider that was passed to connect(). - // Will be a no-op if connect() has not yet been called successfully. - // This method reinitializes the Session object back to the state it was in just after being created. - // Retains the serviceURI, authenticationModel, and name values. - // Delete any of the object's data that had been persisted (for example, to sessionStorage to support - // page refresh). - // Data for any catalogs loaded by the JSDOSession will NOT be deleted. - // See additional commecnts at the Session._disconnect method. - this.disconnect = function () { - var deferred = $.Deferred(), - errorObject; - - try { - _pdsession.subscribe('afterDisconnect', genericSessionEventHandler, this); - - _pdsession._disconnect(deferred); - } catch (e) { - // JSDOSession: Unexpected error calling disconnect: {e.message} - errorObject = new Error(progress.data._getMsgText("jsdoMSG049", "JSDOSession", "disconnect", e.message)); - } - - if (errorObject) { - throw errorObject; - } else { - return deferred.promise(); - } - }; - - this.addCatalog = function (catalogURI, unameOrOpts, password, opts) { - var deferred = $.Deferred(), - catalogURIs, - numCatalogs, - catalogIndex, - addResult, - errorObject, - iOSBasicAuthTimeout, - username, - options, - authProvider; - - if (this._isInvalidated) { - // JSDOSession: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); - } - - // check whether 1st param is a string or an array - if (typeof catalogURI === "string") { - catalogURIs = [catalogURI]; - } else if (catalogURI instanceof Array) { - catalogURIs = catalogURI; - } else { - throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "addCatalog", - "The first argument must be a string or an array of strings specifying the URI of the catalog.")); - } - - // type check the 2nd param if it exists - if (unameOrOpts) { - if (typeof unameOrOpts === "string") { - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // Session: Cannot pass username and password to addCatalog when - // authenticationModel is SSO. Pass an AuthenticationProvider instead. - throw new Error(progress.data._getMsgText("jsdoMSG058", 'Session')); - } - username = unameOrOpts; - // explictly ignore any authProvider if using the (catURI, uname, pw, options) signature - if (opts) { - options = opts; - options.authProvider = undefined; - } - } else if (typeof unameOrOpts === "object") { - options = unameOrOpts; - } else { - // JSDOSession: Argument 2 must be of type object in addCatalog call. - throw new Error(progress.data._getMsgText("jsdoMSG121", "JSDOSession", "2", - "object", "addCatalog")); - } - } - - if (typeof options === 'object') { - // possible override for the workaround for the Cordova iOS async Basic auth bug - iOSBasicAuthTimeout = options.iOSBasicAuthTimeout; - if (options.authProvider) { - authProvider = options.authProvider; - } else if (this.authProvider) { - authProvider = this.authProvider; - } - } - - // Error out if no authProvider or username was given - if (!authProvider && !this.authProvider && !username) { - throw new Error(progress.data._getMsgText("jsdoMSG511")); - } - - /* When we're done processing all catalogs, we pass an array of results to resolve() or - reject(). We're attaching this array to the deferred object, in case the app makes - multiple addCatalog calls (if the array was attached to the JSDOSession, - the 2nd call might overwrite the first) - */ - - /* Add properties to the deferred object for this call to store the total - number of catalogs that are to be done, the number that ahve been processed, - and a reference to an array of results. - Loop through the array of catalogURIs, calling addCatalog for each one. If a call - throws an error or returns something other than ASYNC_PENDING, create a result object - for that catalog and add the result object to the resultArray. Otherwise, the result - object will be added by the afterAddCatalog handler. - If all of the Session.addCatalog calls throw an error or return something other - than ASYNC_PENDING, this function will reject the promise and return. Otherwise - the afterAddCatalog handler will resolve or reject the promise after all calls have - been processed. - Note that we try to make sure that each entry in the results array is in the same position - as its catalogURI in the input array. - */ - // if a catalogURI has no protocol, pdsession will assume it's relative to the serviceURI, - // if there has been a login - // NOTE: this means if the app is trying to load a local catalog, it MUST - // specify the file: protocol (and we need to make sure that works on all platforms) - - _pdsession.subscribe('afterAddCatalog', onAfterAddCatalog, this); - - numCatalogs = catalogURIs.length; - deferred._numCatalogs = numCatalogs; - deferred._numCatalogsProcessed = 0; - deferred._results = []; - deferred._results.length = numCatalogs; - - for ( catalogIndex = 0; catalogIndex < numCatalogs; catalogIndex += 1) { - errorObject = undefined; - addResult = undefined; - try { - addResult = _pdsession.addCatalog( - { catalogURI : catalogURIs[catalogIndex], - async : true, - userName : username, - password : password, - deferred : deferred, - catalogIndex : catalogIndex, - iOSBasicAuthTimeout : iOSBasicAuthTimeout, - authProvider : authProvider, - offlineAddCatalog : true } ); // OK to get catalog if offline - } - catch (e) { - errorObject = new Error("JSDOSession: Unable to send addCatalog request. " + e.message); - } - - if ( addResult !== progress.data.Session.ASYNC_PENDING ) { - /* Set a property on the deferred to indicate that the "overall" result was - a failure. When we decide whether to reject or resolve the promise, we reject - if it's set to GENERAL_FAILURE, otherwise we resolve the promise - (really only need to set this once, but simpler code if we just set (or possibly - re-set) it whenever we find an error, plus if, at some point while we're still - processing, it's important to know whether we've already had an error, we can - check the property) - */ - deferred._overallCatalogResult = progress.data.Session.GENERAL_FAILURE; - if ( errorObject ) { - addResult = progress.data.Session.GENERAL_FAILURE; - } - deferred._results[catalogIndex] = { catalogURI : catalogURIs[catalogIndex], - result : addResult, - errorObject : errorObject, - xhr : undefined }; - deferred._numCatalogsProcessed += 1; - } - } - - if ( (deferred._numCatalogsProcessed === numCatalogs) && !deferred._processedPromise ) { - /* The goal here is to handle the case where all the catalogs - have been processed but the afterAddCatalog handler may not be invoked at the - end (the obvious example is if there are no async requests actually made by - Session.addCatalog). In that case, we have to resolve/reject from here. Chances are - very good that if we're doing this here, there's been at least one error, but just - to be sure, we check the deferred._overallCatalogResult anyway - */ - if ( deferred._overallCatalogResult === progress.data.Session.GENERAL_FAILURE ) { - deferred.reject( this, progress.data.Session.GENERAL_FAILURE, deferred._results ); - } - else { - deferred.resolve( this, progress.data.Session.SUCCESS, deferred._results ); - } - } - - return deferred.promise(); - }; - - // Note that this will work for either of these cases: - // - app originally called JSDOSession.login (so we implicitly created the AuthenticationProvider) - // - app created an AuthenticationProvider and passed it to connect, but now for some reason has - // called logout (this is actually a nice shortcut for someone who has used getSession) - // (NB: we should not allow this for SSO, tho) - // - // Note that we also don't support login/logout on the JSDOSession for page refresh - this.logout = function(){ - var deferred = $.Deferred(), - authProv = this.authProvider; - - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // JSDOSession: Cannot call logout() when authenticationModel is SSO. - // Please use the AuthenticationProvider object instead. - throw new Error(progress.data._getMsgText("jsdoMSG057", - 'JSDOSession', - 'logout()')); - } - - this.disconnect() - .then(function () { - if (authProv) { - return authProv.logout(); - } - // if there's no AP, just resolve immediately - deferred.resolve(that, progress.data.Session.SUCCESS, {}); - }) - .then(function (jsdosession, result, info) { - deferred.resolve(that, result, info); - }, - // catches errors on either login or connect - function (provider, result, info) { - deferred.reject(that, result, info); - } - ); - - return deferred.promise(); - }; - - this.invalidate = function () { - _pdsession.invalidate(); - return this.logout(); - }; - - this.ping = function() { - var deferred = $.Deferred(); - - if (this._isInvalidated) { - // JSDOSession: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); - } - - try { - _pdsession.ping( {async: true, - deferred : deferred, - onCompleteFn : onPingComplete } ); - } - catch(e) { - throw new Error("JSDOSession: Unable to send ping request. " + e.message); - } - - return deferred.promise(); - }; - - // Determine whether the JSDOSession can currently access its web application. - // The use expected for this method is to determine whether a JSDOSession that has - // previously authenticated to its web application still has authorization. - // For example, if the JSDOSession is using Form authentication, is the server - // session still valid or did it expire? - this.isAuthorized = function () { - var deferred = $.Deferred(), - xhr = new XMLHttpRequest(), - result, - that = this; - - if (this._isInvalidated) { - // JSDOSession: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); - } - - // If we logged in successfuly using login() or if we have an AuthProvider, make the call - if (this.loginResult === progress.data.Session.LOGIN_SUCCESS || this.authProvider) { - _pdsession._openRequest(xhr, "GET", _pdsession.loginTarget, true, - function () { - xhr.onreadystatechange = function () { - // do we need this xhr var? The one declared in isAuthorized seems to be in scope - var xhr = this, - cbresult, - fakePingArgs, - info; - - if (xhr.readyState === 4) { - info = {xhr: xhr, - offlineReason: undefined, - fireEventIfOfflineChange: true, - usingOepingFormat: false - }; - - // call _processPingResult because it has logic for - // detecting change in online/offline state - _pdsession._processPingResult(info); - - if (xhr.status >= 200 && xhr.status < 300) { - deferred.resolve(that, - progress.data.Session.SUCCESS, - info); - } else { - if (xhr.status === 401) { - cbresult = progress.data.AuthenticationProvider._getAuthFailureReason(xhr); - } else { - cbresult = progress.data.Session.GENERAL_FAILURE; - } - deferred.reject(that, cbresult, info); - } - } - }; - - try { - xhr.send(); - } catch (e) { - throw new Error("JSDOSession: Unable to validate authorization. " + e.message); - } - } - ); - } else { - // Never logged in (or logged in and logged out). Regardless of what the reason - // was that there wasn't a login, the bottom line is that authentication is required - result = progress.data.Session.LOGIN_AUTHENTICATION_REQUIRED; - deferred.reject(that, result, {xhr: xhr}); - } - - return deferred.promise(); - }; - - /* - set the properties that are passed between client and Web application in the - X-CLIENT-PROPS header. This sets the complete set of properties all at once; - it replaces any existing context - */ - this.setContext = function( context ) { - _pdsession._contextProperties.setContext( context ); - }; - - /* - * Set or remove an individual property in the set of the properties that are passed - * between client and Web application in the X-CLIENT-PROPS header. This operates only - * on the property identiofied by propertyName; all other existing properties remain - * as they are. - * If the propertyName is not part of the context, this call adds it - * If it is part of the context, this call updates it, unless - - * If propertyValue is undefined, this call removes the property - */ - this.setContextProperty = function( propertyName, propertyValue) { - _pdsession._contextProperties.setContextProperty( propertyName, propertyValue ); - }; - - /* - * get the set of properties that are passed between client and Web application in the - * X-CLIENT-PROPS header. Returns an object that has the properties - */ - this.getContext = function( ) { - return _pdsession._contextProperties.getContext(); - }; - - /* get the value of an individual property that is in the set of properties passed between - * client and Web application in the X-CLIENT-PROPS header - */ - this.getContextProperty = function( propertyName) { - return _pdsession._contextProperties.getContextProperty( propertyName ); - }; - - - this._onlineHandler = function( session, request ) { - that.trigger( "online", that, request ); - }; - - this._offlineHandler = function( session, offlineReason, request ) { - that.trigger( "offline", that, offlineReason, request ); - }; - - // PROCESS CONSTRUCTOR ARGUMENTS - // validate constructor input arguments - if ( (arguments.length > 0) && (typeof(arguments[0]) === 'object') ) { - - // (options is the name of the arguments[0] parameter) - if (options.serviceURI && (typeof(options.serviceURI) === "string" ) ) { - _serviceURI = options.serviceURI; - } - else { - throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", - "The options parameter must include a 'serviceURI' property that is a string.") ); - } - - if (options.authenticationModel) { - if (typeof(options.authenticationModel) !== "string" ) { - throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", - "The authenticationModel property of the options parameter must be a string.") ); - } - - options.authenticationModel = options.authenticationModel.toLowerCase(); - } else { - options.authenticationModel = progress.data.Session.AUTH_TYPE_ANON; - } - - // TODO: clean this up. Maybe make an immediate function - if (options.authProvider) { - if (typeof options.authProvider !== 'object') { - // JSDOSession: The 'options' parameter passed to the 'constructor' function - // has an invalid value for the 'authProvider' property. - throw new Error(progress.data._getMsgText( - "jsdoMSG502", - "JSDOSession", - "options", - "constructor", - "authProvider" - )); - } - - if ((options.authProvider.authenticationModel !== progress.data.Session.AUTH_TYPE_FORM_SSO - && options.authProvider.authenticationModel !== options.authenticationModel) || - (options.authProvider.authenticationModel === progress.data.Session.AUTH_TYPE_FORM_SSO - && options.authenticationModel !== progress.data.Session.AUTH_TYPE_SSO)) { - // JSDOSession: Error in constructor. The authenticationModels of the " + - // AuthenticationProvider ({2}) and the JSDOSession ({3}) were not compatible."; - throw new Error(progress.data._getMsgText("jsdoMSG059", "JSDOSession", - options.authProvider.authenticationModel, options.authenticationModel)); - } - // Check if the provider exposes the required API. - if (typeof options.authProvider.hasClientCredentials === 'function') { - if (!options.authProvider.hasClientCredentials()) { - // JSDOSession: The AuthenticationProvider is not managing valid credentials. - throw new Error(progress.data._getMsgText("jsdoMSG125", "JSDOSession")); - } - } else { - // JSDOSession: AuthenticationProvider objects must have a hasClientCredentials method. - throw new Error(progress.data._getMsgText("jsdoMSG505", - "JSDOSession", - "AuthenticationProvider", - "hasClientCredentials")); - } - } else if (options.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // JSDOSession: If a JSDOSession object is using the SSO authentication model, - // the options object passed to its constructor must include an authProvider property. - throw new Error(progress.data._getMsgText("jsdoMSG508")); - } - - } - else { - throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", - "The options argument was missing or invalid.") ); - } - - _name = options.name; - - _pdsession = new progress.data.Session({_storageKey: _name, - _silent: true, - authenticationModel: options.authenticationModel, - serviceURI: options.serviceURI, - jsdosession: this, - authProvider: options.authProvider}); - - try { - if (options.context) { - this.setContext(options.context); - } - _pdsession.subscribe( "online", this._onlineHandler, this); - _pdsession.subscribe( "offline", this._offlineHandler, this); - } catch (err) { - _pdsession = undefined; // so it will be garbage collected - throw err; - } - - }; // end of JSDOSession - -//set up inheritance for JSDOSession -- specifically for incorporating an Observable object - progress.data.JSDOSession.prototype = new progress.util.Observable(); - progress.data.JSDOSession.prototype.constructor = progress.data.JSDOSession; - function validateJSDOSessionSubscribe(args, evt, listenerData) { - listenerData.operation = undefined; - var found = false; - - // make sure this event is one that we support - for (var i = 0; i < this._eventNames.length; i++) { - if (evt === this._eventNames[i].toLowerCase()) { - found = true; - break; - } - } - if (!found) { - throw new Error(progress.data._getMsgText("jsdoMSG042", evt)); - } - - if (args.length < 2) { - throw new Error(progress.data._getMsgText("jsdoMSG038", 2)); - } - - if (typeof args[0] !== 'string') { - throw new Error(progress.data._getMsgText("jsdoMSG039")); - } - - if (typeof args[1] !== 'function') { - throw new Error(progress.data._getMsgText("jsdoMSG040")); - } - else { - listenerData.fn = args[1]; - } - - if (args.length > 2) { - if (typeof args[2] !== 'object') { - throw new Error(progress.data._getMsgText("jsdoMSG041", evt)); - } - else { - listenerData.scope = args[2]; - } - } - } - // events supported by JSDOSession - progress.data.JSDOSession.prototype._eventNames = - ["offline", "online"]; - // callback to validate subscribe and unsubscribe - progress.data.JSDOSession.prototype.validateSubscribe = validateJSDOSessionSubscribe; - progress.data.JSDOSession.prototype.toString = function (radix) { - return "progress.data.JSDOSession"; - }; - - progress.data.getSession = function (options) { - var deferred = $.Deferred(), - authProvider, - promise, - authProviderInitObject = {}; - - // This is the reject handler for session-related operations - // login, addCatalog, and logout - function sessionRejectHandler(originator, result, info) { - // undo the AuthenticationProvider's login if it succeeded - if (authProvider && authProvider.hasClientCredentials()) { - authProvider.logout() - .always(function () { - deferred.reject(result, info); - }); - } else { - deferred.reject(result, info); - } - } - - // This is the reject handler for the login callback - function callbackRejectHandler(reason) { - deferred.reject(progress.data.Session.GENERAL_FAILURE, {"reason": reason}); - } - - function loginHandler(provider) { - var jsdosession; - - try { - jsdosession = new progress.data.JSDOSession(options); - try { - jsdosession.isAuthorized() - .then(function() { - return jsdosession.addCatalog(options.catalogURI); - }, sessionRejectHandler) - .then(function (jsdosession, result, info) { - deferred.resolve(jsdosession, progress.data.Session.SUCCESS); - }, sessionRejectHandler); - } catch (e) { - sessionRejectHandler(jsdosession, - progress.data.Session.GENERAL_FAILURE, - {errorObject: e}); - } - } catch (e) { - sessionRejectHandler(jsdosession, - progress.data.Session.GENERAL_FAILURE, - {errorObject: e}); - } - } - - // This function calls login using credentials from the appropriate source - // Note that as currently implemented, this should NOT be called when - // ANONYMOUS auth is being used, because it unconditionally returns - // AUTHENTICATION_FAILURE if there are no credentials and no loginCallback - function callLogin(provider) { - var errorObject; - - // Use the login callback if we are passed one - // NOTE: Do we even use logincallback? Remove this??? - if (typeof options.loginCallback !== 'undefined') { - options.loginCallback() - .then(function (result) { - try { - provider.login(result.username, result.password) - .then(loginHandler, sessionRejectHandler); - } catch (e) { - sessionRejectHandler( - provider, - progress.data.Session.GENERAL_FAILURE, - { - errorObject: e - } - ); - } - }, callbackRejectHandler); - } else if (options.username && options.password) { - try { - provider.login(options.username, options.password) - .then(loginHandler, sessionRejectHandler); - } catch (e) { - sessionRejectHandler( - provider, - progress.data.Session.GENERAL_FAILURE, - { - errorObject: e - } - ); - } - } else { - // getSession(): The login method was not executed because no credentials were supplied. - errorObject = new Error(progress.data._getMsgText( - "jsdoMSG052", - "getSession()" - )); - sessionRejectHandler( - provider, - progress.data.Session.AUTHENTICATION_FAILURE, - { - // including an Error object to make clear why there is no xhr (normally there would - // be one for an authentication failure) - errorObject: errorObject - } - ); - } - } - - if (typeof options !== 'object') { - // getSession(): 'options' must be of type 'object' - throw new Error(progress.data._getMsgText( - "jsdoMSG503", - "getSession()", - "options", - "object" - )); - } - - if (typeof options.loginCallback !== 'undefined' && - typeof options.loginCallback !== 'function') { - // getSession(): 'options.loginCallback' must be of type 'function' - throw new Error(progress.data._getMsgText( - "jsdoMSG503", - "getSession()", - "options.loginCallback", - "function" - )); - } - - // Create the AuthenticationProvider and let it handle the argument parsing - try { - // If authenticationURI is not set, use serviceURI (except for SSO) - // Note: the test will of course catch any value that evaluates to false, not just undefined or - // null (which are the main concern), but that's probably OK - if (options.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - if (!options.authenticationURI || !options.authProviderAuthenticationModel) { - // "progress.data.getSession: If the getSession method is passed AUTH_TYPE_SSO as - // the authenticationModel, it must also be passed an authenticationURI and an - // authProviderAuthenticationModel." - throw new Error(progress.data._getMsgText("jsdoMSG509")); - } - } - - if (options.authenticationURI) { - authProviderInitObject.uri = options.authenticationURI; - authProviderInitObject.authenticationModel = options.authProviderAuthenticationModel; - - // if auth uri has been passed, there must be an authProviderAuthenticationModel - if (typeof authProviderInitObject.authenticationModel !== "string") { - // JSDOSession: The 'object' parameter passed to the 'getSession' function - // has an invalid value for the 'authProviderAuthenticationModel' property. - throw new Error(progress.data._getMsgText( - "jsdoMSG502", - "progress.data.getSession", - "object", - "getSession", - "authProviderAuthenticationModel" - )); - } - } else { - authProviderInitObject.uri = options.serviceURI; - authProviderInitObject.authenticationModel = options.authenticationModel; - } - - authProvider = new progress.data.AuthenticationProvider(authProviderInitObject); - options.authProvider = authProvider; - - if (authProvider.hasClientCredentials()) { - loginHandler(authProvider); - } else { - // If model is anon, just log in. - if (authProvider.authenticationModel === progress.data.Session.AUTH_TYPE_ANON) { - authProvider.login() - .then(loginHandler, sessionRejectHandler); - } else { - // We need to log-in with credentials. - callLogin(authProvider); - } - } - } catch (error) { - // throw error; - sessionRejectHandler( - null, - progress.data.Session.GENERAL_FAILURE, - { - errorObject: error - } - ); - } - - return deferred.promise(); - }; - - progress.data.invalidateAllSessions = function () { - var jsdosession, - key, - deferred = $.Deferred(), - jsdosessions = progress.data.ServicesManager._jsdosessions, - invalidatePromises = []; - - for (key in jsdosessions) { - if (jsdosessions.hasOwnProperty(key)) { - jsdosession = jsdosessions[key]; - - invalidatePromises.push(jsdosession.invalidate()); - } - } - - $.when.apply($, invalidatePromises) - .then(function () { - deferred.resolve(progress.data.Session.SUCCESS); - }, function (session, result, info) { - deferred.reject(progress.data.Session.GENERAL_FAILURE, info); - }); - - // Using beautiful jquery shenanigans - return deferred.promise(); - }; - -})(); - -if (typeof exports !== "undefined") { - exports.progress = progress; -} - -//# sourceURL=progress.jsdo.js -/* -progress.auth.js Version: 4.4.0-3 - -Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - "use strict"; // note that this makes JSLint complain if you use arguments[x] - - /*global progress : true*/ - /*global $ : false, storage, XMLHttpRequest*/ - - /* define these if not defined yet - they may already be defined if - progress.js was included first */ - if (typeof progress === "undefined") { - progress = {}; - } - if (typeof progress.data === "undefined") { - progress.data = {}; - } - - // This is really more along the lines of a Factory method in that it explicitly creates an object - // and returns it based on the the authModel parameter (rather than following the default JS - // pattern of adding properties to the "this" object created for it and passed in by the runtime). - // NOTE: If we support multiple AuthenticationProviders that get different tokens from the same - // server, we may need to add a "name" property to the initObject to use as a storage key - - progress.data.AuthenticationProvider = function (initObject) { - var authProv, - authModel, - uri; - - // process constructor arguments - if (typeof initObject === 'object') { - - // these 2 calls throw an appropriate error if the check doesn't pass - this._checkStringArg( - "constructor", - initObject.authenticationModel, - "initObject.authenticationModel", - "initObject.authenticationModel" - ); - - this._checkStringArg( - "constructor", - initObject.uri, - "init-object.uri", - "init-object.uri" - ); - } else { - // AuthenticationProvider: Invalid signature for constructor. The init-object argument - // was missing or invalid. - throw new Error(progress.data._getMsgText( - "jsdoMSG033", - "AuthenticationProvider", - "the constructor", - "The init-object argument was missing or invalid." - )); - } - - authModel = initObject.authenticationModel.toLowerCase(); - switch (authModel) { - case progress.data.Session.AUTH_TYPE_ANON: - this._initialize(initObject.uri, progress.data.Session.AUTH_TYPE_ANON, - {"_loginURI": progress.data.AuthenticationProvider._homeLoginURIBase}); - authProv = this; - break; - case progress.data.Session.AUTH_TYPE_BASIC: - authProv = new progress.data.AuthenticationProviderBasic(initObject.uri); - break; - case progress.data.Session.AUTH_TYPE_FORM: - authProv = new progress.data.AuthenticationProviderForm(initObject.uri); - break; - case progress.data.Session.AUTH_TYPE_FORM_SSO: - authProv = new progress.data.AuthenticationProviderSSO(initObject.uri); - break; - default: - // AuthenticationProvider: The 'init-object' parameter passed to the 'constructor' function - // has an invalid value for the 'authenticationModel' property. - throw new Error(progress.data._getMsgText( - "jsdoMSG502", - "AuthenticationProvider", - "init-object", - "constructor", - "authenticationModel" - )); - //break; - } - - return authProv; - }; - - - // ADD METHODS TO THE AuthenticationProvider PROTOYPE - - // GENERIC IMPLEMENTATION FOR login METHOD THAT THE API IMPLEMENTATIONS OF login CAN CALL - // (technically, they don't override it, they each have small login methods that call this) - progress.data.AuthenticationProvider.prototype._loginProto = - function (sendParam) { - var deferred = $.Deferred(), - xhr, - uriForRequest, - header, - that = this; - - if (this._loggedIn) { - // "The login method was not executed because the AuthenticationProvider is - // already logged in." - throw new Error(progress.data._getMsgText("jsdoMSG051", "AuthenticationProvider")); - } - - xhr = new XMLHttpRequest(); - - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - // process the response from the Web application - that._processLoginResult(xhr, deferred); - } - }; - - if (progress.data.Session._useTimeStamp) { - uriForRequest = progress.data.Session._addTimeStampToURL(this._loginURI); - } else { - uriForRequest = this._loginURI; - } - - this._openLoginRequest(xhr, uriForRequest); - - // We specify application/json for the response so that, if a bad request is sent, an - // OE Web application will directly send back a 401 with error info in the body as JSON. - // So we force the accept header to application/json because if we make an anonymous - // request to a FORM/BASIC backend, it might redirect us to a login page since we have - // no credentials. And since we can technically access JUST the login page, the XHR - // will identify it as SUCCESS. If we specify "application/json", no redirects will - // happen, just a plain old "401 GET OUTTA HERE" code. - xhr.setRequestHeader("Accept", "application/json"); - - xhr.send(sendParam); - return deferred.promise(); - }; - - - - // PUBLIC METHODS (and their "helpers") (documented as part of the JSDO library API) - - // login API method -- just a shell that calls loginProto - progress.data.AuthenticationProvider.prototype.login = function () { - return this._loginProto(); - }; - - // HELPER FOR login METHOD, PROBABLY OVERRIDDEN IN MOST CONSTRUCTORS - progress.data.AuthenticationProvider.prototype._openLoginRequest = function (xhr, uri) { - xhr.open('GET', uri, true); - progress.data.Session._setNoCacheHeaders(xhr); - }; - - // HELPER FOR login METHOD, PROBABLY OVERRIDDEN IN MOST CONSTRUCTORS - progress.data.AuthenticationProvider.prototype._processLoginResult = function (xhr, deferred) { - var result; - - if (xhr.status === 200) { - // Need to set loggedIn now so we can call logout from here if there's an - // error processing the response (e.g., authentication succeeded but we didn't get a - // token for some reason) - this._loggedIn = true; - this._storeInfo(); - result = progress.data.Session.SUCCESS; - } else if (xhr.status === 401) { - // If this is Anonymous, somebody gave us the wrong authenticationModel! - result = progress.data.Session.AUTHENTICATION_FAILURE; - } else { - result = progress.data.Session.GENERAL_FAILURE; - } - - this._settlePromise(deferred, result, {"xhr": xhr}); - }; - - - // logout API METHOD -- SOME CONSTRUCTORS OR PROTOTYPES WILL OVERRIDE THIS - progress.data.AuthenticationProvider.prototype.logout = function () { - var deferred = $.Deferred(); - - this._reset(); - deferred.resolve(this, progress.data.Session.SUCCESS, {}); - return deferred.promise(); - }; - - // hasClientCredentials API METHOD -- PROBABLY ONLY OVERRIDDEN BY SSO - progress.data.AuthenticationProvider.prototype.hasClientCredentials = function () { - return this._loggedIn; - }; - - // hasRefreshToken API METHOD -- returns false for all AutghenticationProvider types except SSO, - // which overrides it - progress.data.AuthenticationProvider.prototype.hasRefreshToken = function () { - return false; - }; - - // QUASI-PUBLIC METHOD - - // general-purpose method for opening requests (mainly for jsdo calls) - // This method is not part of the documented API that a developer would - // program against, but it gets used in a validation check by the JSDOSESSION, because the - // JSDOSESSION code expects it to be present. The point here is that if a developer were to - // create their own AuthenticationProvider object, it would need to include this method - // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change - // it to use promises - progress.data.AuthenticationProvider.prototype._openRequestAndAuthorize = function (xhr, - verb, - uri, - async, - callback) { - var errorObject; - - if (this.hasClientCredentials()) { - xhr.open(verb, uri, async); - - // Check out why we do this in _loginProto - xhr.setRequestHeader("Accept", "application/json"); - } else { - // AuthenticationProvider: The AuthenticationProvider is not managing valid credentials. - errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); - } - - callback(errorObject); - }; - - // GENERAL PURPOSE "INTERNAL" METHODS, NOT RELATED TO SPECIFIC API ELEMENTS - // (not documented, intended for use only within the JSDO library) - - // General purpose method for initializing an object - progress.data.AuthenticationProvider.prototype._initialize = function (uriParam, - authModel, - targetURIs) { - var tempURI, - target; - - Object.defineProperty(this, 'uri', - { - get: function () { - return this._uri; - }, - enumerable: true - }); - - Object.defineProperty(this, 'authenticationModel', - { - get: function () { - return this._authenticationModel; - }, - enumerable: true - }); - - - // get rid of trailing '/' because appending service url that starts with '/' - // will cause request failures - if (uriParam[uriParam.length - 1] === "/") { - tempURI = uriParam.substring(0, uriParam.length - 1); - } else { - tempURI = uriParam; - } - - // take the modified authentication uri and prepend it to all of the targets passed - // in. E.g., the targetURIs object will include a "loginURI" property that has the - // uri segment which is to be added to the auth uri for logging in - for (target in targetURIs) { - if (targetURIs.hasOwnProperty(target)) { - this[target] = tempURI + targetURIs[target]; - } - } - - this._authenticationModel = authModel; - this._uri = uriParam; // keep the uri property the same as what was passed in - - this._loggedIn = false; - this._dataKeys = { - uri: ".uri", - loggedIn: ".loggedIn" - }; - - // future: for page refresh -- storeSessionInfo("authenticationModel", authenticationModel); - - if (typeof sessionStorage === "undefined") { - // "AuthenticationProvider: No support for sessionStorage." - throw new Error(progress.data._getMsgText("jsdoMSG126", - "AuthenticationProvider", - "sessionStorage")); - } - // if you switch to a different type of storage, change the error message argument above - this._storage = sessionStorage; - - // maybe should come up with something more intelligent than this - this._storageKey = this._uri; // or name - this._dataKeys.uri = this._storageKey + this._dataKeys.uri; - this._dataKeys.loggedIn = this._storageKey + this._dataKeys.loggedIn; - - if (this._retrieveLoggedIn()) { - this._loggedIn = true; - } - }; - - - // Store data in storage with the uri as the key. setItem() throws. (Should add an - // option for the developer to specify the key) - // a "QuotaExceededError" error if there is insufficient storage space or - // "the user has disabled storage for the site" (Web storage spec at WHATWG) - progress.data.AuthenticationProvider.prototype._storeInfo = function () { - this._storage.setItem(this._dataKeys.uri, JSON.stringify(this._uri)); - this._storage.setItem(this._dataKeys.loggedIn, JSON.stringify(this._loggedIn)); - }; - - // Get a piece of state data from storage. Returns null if the item isn't in storage - progress.data.AuthenticationProvider.prototype._retrieveInfoItem = function (propName) { - var jsonStr = this._storage.getItem(propName), - value = null; - - if (jsonStr !== null) { - try { - value = JSON.parse(jsonStr); - } catch (e) { - value = null; - } - } - return value; - }; - - // Get an AuthenticationProvider's uri from storage - progress.data.AuthenticationProvider.prototype._retrieveURI = function () { - return this._retrieveInfoItem(this._dataKeys.uri); - }; - - // Get an AuthenticationProvider's logon status from storage - progress.data.AuthenticationProvider.prototype._retrieveLoggedIn = function () { - return this._retrieveInfoItem(this._dataKeys.loggedIn); - }; - - // Clear the persistent storage used by an AuthenticationProvider - progress.data.AuthenticationProvider.prototype._clearInfo = function (info) { - this._storage.removeItem(this._dataKeys.uri); - this._storage.removeItem(this._dataKeys.loggedIn); - }; - - // Put the internal state back to where it is when the constructor finishes - // running (so the authentication model and uri are not changed, but other data is reset. - // and storage is cleared out) - progress.data.AuthenticationProvider.prototype._reset = function () { - this._clearInfo(); - this._loggedIn = false; - }; - - - // General purpose utility method, no overrides expected - progress.data.AuthenticationProvider.prototype._settlePromise = function (deferred, result, info) { - if (result === progress.data.Session.SUCCESS) { - deferred.resolve(this, result, info); - } else { - deferred.reject(this, result, info); - } - }; - - // General purpose utility method, no overrides expected - progress.data.AuthenticationProvider.prototype._checkStringArg = function (fnName, - argToCheck, - argPosition, - argName) { - // TODO: ? distinguish between undefined (so we can give developer a clue that they - // may be missing a property) and defined but wrong type - if (typeof argToCheck !== "string") { - // AuthenticationProvider: Argument {param-position} must be of type string in {fnName} call. - throw new Error(progress.data._getMsgText( - "jsdoMSG121", - "AuthenticationProvider", - argPosition, - "string", - fnName - )); - } else if (argToCheck.length === 0) { - // AuthenticationProvider: {param-name} cannot be an empty string. - throw new Error(progress.data._getMsgText( - "jsdoMSG501", - "AuthenticationProvider", - argName, - fnName - )); - } - }; - - - // "STATIC" PROPERTIES AND METHODS -- not on the prototype -- you cannot access these through an - // object created by "new" --- they are properties of the AuthenticationProvider constructor function - - // Takes an XHR as an input. If the xhr status is 401 (Unauthorized), determines whether - // the auth failure was due to an expired token. Returns progress.data.Session.EXPIRED_TOKEN - // if it was, progress.data.Session.AUTHENTICATION_FAILURE if it wasn't, null if the xhr status wasn't 401 - progress.data.AuthenticationProvider._getAuthFailureReason = function (xhr) { - var contentType, - jsonObject, - result = progress.data.Session.AUTHENTICATION_FAILURE; - - if (xhr.status === 401) { - contentType = xhr.getResponseHeader("Content-Type"); - if (contentType && (contentType.indexOf("application/json") > -1) && xhr.responseText) { - jsonObject = JSON.parse(xhr.responseText); - if (jsonObject.error === "sso.token.expired_token") { - result = progress.data.Session.EXPIRED_TOKEN; - } - } - } else { - result = null; - } - return result; - }; - - Object.defineProperty(progress.data.AuthenticationProvider, '_homeLoginURIBase', { - value: "/static/home.html", - enumerable: true - }); - Object.defineProperty(progress.data.AuthenticationProvider, '_springLoginURIBase', { - value: "/static/auth/j_spring_security_check", - enumerable: true - }); - Object.defineProperty(progress.data.AuthenticationProvider, '_springLogoutURIBase', { - value: "/static/auth/j_spring_security_logout", - enumerable: true - }); - Object.defineProperty(progress.data.AuthenticationProvider, '_springFormTokenLoginURIBase', { - value: progress.data.AuthenticationProvider._springLoginURIBase + "?OECP=yes", - enumerable: true - }); - Object.defineProperty(progress.data.AuthenticationProvider, '_springFormTokenRefreshURIBase', { - value: "/static/auth/token?op=refresh", - enumerable: true - }); - -}()); - -//# sourceURL=progress.jsdo.js -/* -progress.auth.basic.js Version: 4.4.0-3 - -Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - "use strict"; // note that this makes JSLint complain if you use arguments[x] - - /*global progress : true*/ - /*global $ : false, storage, XMLHttpRequest, msg, btoa*/ - - progress.data.AuthenticationProviderBasic = function (uri) { - var defaultiOSBasicAuthTimeout, // TO DO: need to implement the use of this - userName = null, - password = null, - fn; - - // process constructor arguments, etc. - this._initialize(uri, progress.data.Session.AUTH_TYPE_BASIC, - {"_loginURI": progress.data.AuthenticationProvider._homeLoginURIBase}); - - // PRIVATE FUNCTIONS - - // from http://coderseye.com/2007/how-to-do-http-basic-auth-in-ajax.html - function make_basic_auth_header(user, pw) { - var tok = user + ':' + pw, - hash = btoa(tok); - return "Basic " + hash; - } - - // "INTERNAL" METHODS - // Override the protoype's method but call it from within the override - // (Define the override here in the constructor so it has access to instance variables) - this._reset = function () { - userName = null; - password = null; - progress.data.AuthenticationProviderBasic.prototype._reset.apply(this); - }; - - // Override the protoype's method (this method does not invoke the prototype's copy) - // (Define the override here in the constructor so it has access to instance variables) - this._openLoginRequest = function (xhr, uri) { - var auth; - - xhr.open("GET", uri, true); // but see comments below inside the "if userName" - // may have to go with that approach - - if (userName) { - - // set Authorization header - auth = make_basic_auth_header(userName, password); - xhr.setRequestHeader('Authorization', auth); - } - - progress.data.Session._setNoCacheHeaders(xhr); - }; - - // Override the protoype's method but call it from within the override - // (Define the override here in the constructor so it has access to instance variables) - this._processLoginResult = function _basic_processLoginResult(xhr, deferred) { - progress.data.AuthenticationProviderBasic.prototype._processLoginResult.apply( - this, - [xhr, deferred] - ); - if (!this._loggedIn) { - // login failed, clear the credentials - userName = null; - password = null; - } - }; - - // Override the protoype's method (this method does not invoke the prototype's copy, but - // calls a prototype general-purpose login method) - // (Define the override here in the constructor so it has access to instance variables) - this.login = function (userNameParam, passwordParam) { - // these throw if the check fails (may want to do something more elegant) - this._checkStringArg("login", userNameParam, 1, "userName"); - this._checkStringArg("login", passwordParam, 2, "password"); - - userName = userNameParam; - password = passwordParam; - return this._loginProto(); - }; - - // Override the protoype's method (this method does not invoke the prototype's copy) - // (Define the override here in the constructor so it has access to instance variables) - // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change - // it to use promises - this._openRequestAndAuthorize = function (xhr, verb, uri, async, callback) { - var auth, - errorObject; - - if (this.hasClientCredentials()) { - - xhr.open(verb, uri, async); // but see comments below inside the "if userName" - // may have to go with that approach - - if (userName) { - - // set Authorization header - auth = make_basic_auth_header(userName, password); - xhr.setRequestHeader('Authorization', auth); - } - - progress.data.Session._setNoCacheHeaders(xhr); - } else { - // AuthenticationProvider: The AuthenticationProvider is not managing valid credentials. - errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); - } - - callback(errorObject); - }; - }; - - - // Give this constructor the prototype from the "base" AuthenticationProvider - // Do this indirectly by way of an intermediate object so changes to the prototype ("method overrides") - // don't affect other types of AuthenticationProviders that use the prototype) - function BasicProxy() {} - BasicProxy.prototype = progress.data.AuthenticationProvider.prototype; - progress.data.AuthenticationProviderBasic.prototype = new BasicProxy(); - - // Reset the prototype's constructor property so it points to AuthenticationProviderForm rather than - // the one that it just inherited (this is pretty much irrelevant though - the correct constructor - // will get called regardless) - progress.data.AuthenticationProviderBasic.prototype.constructor = - progress.data.AuthenticationProviderBasic; - - - // OVERRIDE METHODS ON PROTOTYPE IF NECESSARY AND POSSIBLE - // (SOME METHODS ARE OVERRIDDEN IN THE CONSTRUCTOR BECAUSE THEY NEED ACCESS TO INSTANCE VARIABLES) - - // NOTE: There are no overrides of the following methods (either here or in the constructor). - // This object uses these methods from the original prototype(i.e., the implementations from the - // AuthenticationProvider object): - // logout (API method) - // hasClientCredentials (API method) - -}()); -//# sourceURL=progress.jsdo.js -/* -progress.auth.form.js Version: 4.4.0-3 - -Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - "use strict"; // note that this makes JSLint complain if you use arguments[x] - - /*global progress : true*/ - /*global $ : false, XMLHttpRequest*/ - - var fn; - - progress.data.AuthenticationProviderForm = function (uri) { - - // PROCESS CONSTRUCTOR ARGUMENTS, CREATE API PROPERTIES, ETC. - this._initialize(uri, progress.data.Session.AUTH_TYPE_FORM, - {"_loginURI": progress.data.AuthenticationProvider._springLoginURIBase, - "_logoutURI": progress.data.AuthenticationProvider._springLogoutURIBase - }); - }; - - // Start by giving this constructor the prototype from the "base" AuthenticationProvider - // Do this indirectly by way of an intermediate object so changes to the prototype ("method overrides") - // don't affect other types of AuthenticationProviders that use the prototype) - function FormProxy() {} - FormProxy.prototype = progress.data.AuthenticationProvider.prototype; - progress.data.AuthenticationProviderForm.prototype = - new FormProxy(); - - // Reset the prototype's constructor property so it points to AuthenticationProviderForm rather than - // the one that it just inherited (this is pretty much irrelevant though - the correct constructor - // will get called regardless) - progress.data.AuthenticationProviderForm.prototype.constructor = - progress.data.AuthenticationProviderForm; - - - // OVERRIDE THE "BASE" AuthenticationProvider PROTOYPE METHODS WHERE NECESSARY - - // All of the methods defined here as part of the AuthenticationProviderForm prototype (instead - // of in the AuthenticationProviderForm constructor) can be inherited by the AuthenticationProviderSSO - // prototype without incurring the overhead of creating a full-blown instance of - // AuthenticationProviderForm to serve as the prototype for the SSO constructor. - // Note: if it turns out that any of the methods defined this way need access to internal variables - // of an AuthenticationProviderForm object, they'll need to be moved out of here. - - // NOTE: There are no overrides of the following methods (either here or in the constructor). - // This object uses these methods from the original prototype(i.e., the implementations from the - // AuthenticationProvider object): - // _reset (general-purpose helper) - // hasClientCredentials (API method) - // _processLoginResult (API helper method) - - - // login API METHOD AND "HELPERS" - progress.data.AuthenticationProviderForm.prototype.login = function (userNameParam, passwordParam) { - var deferred = $.Deferred(), - xhr, - that = this; - - // these throw if the check fails (may want to do something more elegant) - this._checkStringArg("login", userNameParam, 1, "userName"); - this._checkStringArg("login", passwordParam, 2, "password"); - - return this._loginProto("j_username=" + encodeURIComponent(userNameParam) + - "&j_password=" + encodeURIComponent(passwordParam) + "&submit=Submit"); - }; - - // login helper - // Override the protoype's method (this method does not invoke the prototype's copy) - // By defining this here, we can have the SSO AuthenticationProvider use it without - // incurring the overhead of creating a Form instance as the prototype for the SSO constructor - progress.data.AuthenticationProviderForm.prototype._openLoginRequest = function (xhr, uri) { - - xhr.open('POST', uri, true); - - xhr.setRequestHeader("Cache-Control", "max-age=0"); - xhr.setRequestHeader("Pragma", "no-cache"); - xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); - - xhr.withCredentials = true; - - }; - - // logout API METHOD AND "HELPERS" - // Override the prototype method and do not call it because the Anonymous AuthenticationProvider - // doesn't make a server call for logout - // (But this method does do what the SSO AuthenticationProvider needs, so keep it on - // the Form prototype if possible) - progress.data.AuthenticationProviderForm.prototype.logout = function () { - var deferred = $.Deferred(), - xhr, - that = this; - - if (!this._loggedIn) { - // logout is regarded as a success if the AuthenticationProvider isn't logged in - deferred.resolve(this, progress.data.Session.SUCCESS, {}); - } else { - xhr = new XMLHttpRequest(); - this._openLogoutRequest(xhr); - - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - // process the response from the Web application - that._processLogoutResult(xhr, deferred); - } - }; - - xhr.send(); - } - - // Unconditionally reset --- even if the actual server request fails, we still want - // to reset this AuthenticationProvider so it can try a login if desired. - // We also reset even in the case where we're not logged in, just in case. - // (In the future we can add a parameter that controls whether the reinit is unconditional, - // if the developer wants to log out of the token server session but contnue to use the token) - this._reset(); - return deferred.promise(); - }; - - // logout helper (there is no version defined in the original protoype) - progress.data.AuthenticationProviderForm.prototype._openLogoutRequest = function (xhr) { - xhr.open('GET', this._logoutURI, true); - xhr.setRequestHeader("Cache-Control", "max-age=0"); - xhr.withCredentials = true; - xhr.setRequestHeader("Accept", "application/json"); - }; - - // logout helper (there is no version defined in the original protoype) - progress.data.AuthenticationProviderForm.prototype._processLogoutResult = function (xhr, deferred) { - var result; - - if (xhr.status === 200) { - result = progress.data.Session.SUCCESS; - } else if (xhr.status === 401) { - // treat this as a success because the most likely cause is that the session expired - // (Note that an 11.7 OE PAS Web application will return a 200 if we log out with - // an expired JSESSIONID, so this code may not be executed anyway) - result = progress.data.Session.SUCCESS; - } else { - result = progress.data.Session.GENERAL_FAILURE; - } - - this._settlePromise(deferred, result, {"xhr": xhr}); - - }; - - // GENERAL PURPOSE METHOD FOR OPENING REQUESTS (MAINLY FOR JSDO CALLS) - // Override the protoype's method but call it from within the override - // Since the override is being put into the constructor's prototype, and - // since it calls the overridden method which had originally been in the prototype, - // we add a "_super" property to the overriding method so it can still access the original method - // (We could just call that method directly in here like this: - // progress.data.AuthenticationProviderProto.prototype._openRequestAndAuthorize - // but if we ever change the place where we get the initial protoype for - // AuthenticationProviderForm from, we would need to remember to change that here. - // The use of the _super property will handle that automatically, plus it was more fun - // to do it this way) - // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change - // it to use promises - fn = progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize; - progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize = - function (xhr, verb, uri, async, callback) { - - function afterSuper(errorObject) { - xhr.withCredentials = true; - callback(errorObject); - } - - try { - progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize._super.apply( - this, - [xhr, verb, uri, async, afterSuper] - ); - } catch (e) { - callback(e); - } - }; - progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize._super = fn; - - -}()); -//# sourceURL=progress.jsdo.js -/* -progress.auth.sso.js Version: 4.4.0-3 - -Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - "use strict"; // note that this makes JSLint complain if you use arguments[x] - - /*global progress : true*/ - /*global $ : false */ - - var fn; - -// ADD AN OPTIONS PARAM THAT CAN INCLUDE A NAME FOR PAGE REFRESH? - progress.data.AuthenticationProviderSSO = function (uri) { - var that = this, - // SSO specific - _automaticTokenRefresh, - temp, - ssoTokenInfo = null, - tokenDataKeys = { // SSO specific - token: ".access_token", - refreshToken: ".refresh_token", - tokenType: ".token_type", - expiration: ".expires_in", - accessTokenExpiration: ".accessTokenExpiration" - }; - - // PRIVATE FUNCTIONS - // (The constructor uses local variables and functions mainly to try to protect the token - // information as much as possible. A few could probably be defined as properties/methods, but - // there's currently no need for that because the AuthenticationProvider API has no objects - // that inherit from AuthenticationProviderSSO.) - - // Store the given token with the uri as the key. setItem() throws - // a "QuotaExceededError" error if there is insufficient storage space or - // "the user has disabled storage for the site" (Web storage spec at WHATWG) - function storeTokenInfo(info) { - var date, - accessTokenExpiration; - - if (info.access_token.length) { - that._storage.setItem(tokenDataKeys.token, JSON.stringify(info.access_token)); - } - if (info.refresh_token.length) { - that._storage.setItem(tokenDataKeys.refreshToken, JSON.stringify(info.refresh_token)); - // The time given for the access token's expiration is in seconds. We transform it - // into milliseconds and add it to date.getTime() for a more standard format. - date = new Date(); - // This should probably be renamed accessTokenRefreshThreshold - accessTokenExpiration = date.getTime() + (info.expires_in * 1000 * 0.75); - that._storage.setItem(tokenDataKeys.accessTokenExpiration, JSON.stringify(accessTokenExpiration)); - } else { - // if there is no refresh token, remove any existing one. This handles the case where - // we got a new token via refresh, but now we're not being given any more refresh tokens - that._storage.removeItem(tokenDataKeys.refreshToken); - that._storage.removeItem(tokenDataKeys.accessTokenExpiration); - } - that._storage.setItem(tokenDataKeys.tokenType, JSON.stringify(info.token_type)); - that._storage.setItem(tokenDataKeys.expiration, JSON.stringify(info.expires_in)); - } - - // get one of the pieces of data related to tokens from storage (could be the token itself, or - // the refresh token, expiration info, etc.). Returns null if the item isn't in storage - function retrieveTokenProperty(propName) { - var jsonStr = that._storage.getItem(propName), - value = null; - - if (jsonStr !== null) { - try { - value = JSON.parse(jsonStr); - } catch (e) { - value = null; - } - } - return value; - } - - function retrieveToken() { - return retrieveTokenProperty(tokenDataKeys.token); - } - - function retrieveRefreshToken() { - return retrieveTokenProperty(tokenDataKeys.refreshToken); - } - - function retrieveAccessTokenExpiration() { - return retrieveTokenProperty(tokenDataKeys.accessTokenExpiration); - } - - function retrieveTokenType() { - return retrieveTokenProperty(tokenDataKeys.tokenType); - } - - // This is going to be hardcoded for now. This can very - // possibly change in the future if we decide to expose - // the token to the user. - function getToken() { - return retrieveToken(); - } - - function retrieveExpiration() { - return retrieveTokenProperty(tokenDataKeys.expiration); - } - - function clearTokenInfo(info) { - that._storage.removeItem(tokenDataKeys.token); - that._storage.removeItem(tokenDataKeys.refreshToken); - that._storage.removeItem(tokenDataKeys.tokenType); - that._storage.removeItem(tokenDataKeys.expiration); - that._storage.removeItem(tokenDataKeys.accessTokenExpiration); - } - - // function is SSO specific - function openRefreshRequest(xhr) { - xhr.open('POST', that._refreshURI, true); - xhr.setRequestHeader("Cache-Control", "max-age=0"); - xhr.withCredentials = true; - xhr.setRequestHeader("Content-Type", "application/json"); - xhr.setRequestHeader("Accept", "application/json"); - } - - // function is SSO specific - function processRefreshResult(xhr, deferred) { - var errorObject, - result, - ssoTokenJSON; - - if (xhr.status === 200) { - // get token and store it; if that goes well, resolve the promise, otherwise reject it - try { - ssoTokenInfo = JSON.parse(xhr.responseText); - - if (ssoTokenInfo.access_token) { - storeTokenInfo(ssoTokenInfo); - // got the token info, its access_token has a value, and storeTokenInfo() - // didn't thrown an error, so call this a success - result = progress.data.Session.SUCCESS; - } else { - result = progress.data.Session.GENERAL_FAILURE; - // {1}: Unexpected error calling refresh: {error-string} - // ( No token returned from server) - errorObject = new Error(progress.data._getMsgText( - "jsdoMSG049", - "AuthenticationProvider", - "refresh", - progress.data._getMsgText("jsdoMSG050") - )); - } - } catch (ex) { - result = progress.data.Session.GENERAL_FAILURE; - // {1}: Unexpected error calling refresh: {error-string} - // (error could be thrown from storeTokenInfo when it calls setItem()) - errorObject = new Error(progress.data._getMsgText( - "jsdoMSG049", - "AuthenticationProvider", - "refresh", - ex.message - )); - } - } else if (xhr.status === 401) { - that._reset(); // treat authentication failure as the equivalent of a logout - result = progress.data.Session.AUTHENTICATION_FAILURE; - } else { - result = progress.data.Session.GENERAL_FAILURE; - } - - that._settlePromise(deferred, result, {"xhr": xhr, - "errorObject": errorObject}); // OK if undefined - } - - - this._processLoginResult = function (xhr, deferred) { - var errorObject, - result, - ssoTokenJSON; - - if (xhr.status === 200) { - // Need to set loggedIn now so we can call logout from here if there's an - // error processing the response (e.g., authentication succeeded but we didn't get a - // token for some reason) - this._loggedIn = true; - - // get token and store it; if that goes well, resolve the promise, otherwise reject it - try { - ssoTokenInfo = JSON.parse(xhr.responseText); - - if (ssoTokenInfo.access_token) { - storeTokenInfo(ssoTokenInfo); - // got the token info, its access_token has a value, and storeTokenInfo() - // didn't throw an error, so call this a success - result = progress.data.Session.SUCCESS; - } else { - result = progress.data.Session.GENERAL_FAILURE; - // {1}: Unexpected error calling login: {error-string} - // ( No token returned from server) - errorObject = new Error(progress.data._getMsgText( - "jsdoMSG049", - "AuthenticationProvider", - "login", - progress.data._getMsgText("jsdoMSG050") - )); - } - } catch (ex) { - result = progress.data.Session.GENERAL_FAILURE; - // {1}: Unexpected error calling login: {error-string} - // (error could be thrown from storeTokenInfo when it calls setItem()) - errorObject = new Error(progress.data._getMsgText( - "jsdoMSG049", - "AuthenticationProvider", - "login", - ex.message - )); - } - - // log out if there was an error processing the response so the app can try to log in again - if (result !== progress.data.Session.SUCCESS) { - // call logout, but ignore its outcome -- just tell caller that login failed - this.logout() - .always(function (authProv) { - authProv._settlePromise(deferred, result, {"xhr": xhr, - "errorObject": errorObject}); - }); - return; // so we don't execute the reject below, which could invoke the fail handler - // before we're done with the logout - } - - } else if (xhr.status === 401) { - result = progress.data.Session.AUTHENTICATION_FAILURE; - } else { - result = progress.data.Session.GENERAL_FAILURE; - } - - this._settlePromise(deferred, result, {"xhr": xhr}); - }; - - - // Override the protoype's method but call it from within the override. (Define the override - // here in the constructor so it has access to the internal function and variable) - this._reset = function () { - progress.data.AuthenticationProviderSSO.prototype._reset.apply(this); - clearTokenInfo(); - ssoTokenInfo = null; - }; - - - // Override the protoype's method but call it from within the override. (Define the override - // here in the constructor so it has access to the internal function getToken() ) - // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change - // it to use promises - this._openRequestAndAuthorize = function (xhr, - verb, - uri, - async, - callback) { - var that = this, - date, - errorObject; - - function afterRefreshCheck(provider, result, info) { - // if refresh failed because of auth failure, we will have gotten rid of the - // token and reset the auth provider - if (result === progress.data.Session.AUTHENTICATION_FAILURE) { - callback(new Error(progress.data._getMsgText("jsdoMSG060"))); - } else { - // We've done the refresh check (and possible refresh) for SSO, now execute - // the base _openRequest... method, which does common things for Form-based - progress.data.AuthenticationProviderSSO.prototype._openRequestAndAuthorize.apply( - that, - [xhr, verb, uri, async, function (errorObject) { - if (!errorObject) { - xhr.setRequestHeader('Authorization', "oecp " + getToken()); - } - callback(errorObject); - }] - ); - } - } - - if (this.hasClientCredentials()) { - // Every token given has an expiration "hint". If the token's lifespan - // is close to or past that limit, then a refresh is done. - // No matter what the outcome of the refresh, keep in mind we always - // send the original request. - date = new Date(); - if (this.automaticTokenRefresh && - this.hasRefreshToken() && - date.getTime() > retrieveAccessTokenExpiration()) { - try { - this.refresh() - .always(function (provider, result, info) { - afterRefreshCheck(provider, result, info); - }); - } catch (e) { - callback(e); - } - } else { - afterRefreshCheck(this, progress.data.Session.SUCCESS, null); - } - } else { - // This message is SSO specific, unless we can come up with a more general message - // JSDOSession: The AuthenticationProvider needs to be managing a valid token. - errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); - callback(errorObject); - } - }; - - - // API METHODS - - // override the prototype's hasClientCredentials method - this.hasClientCredentials = function () { - return (retrieveToken() === null ? false : true); - }; - - - this.refresh = function () { - var deferred = $.Deferred(), - xhr; - - if (!this._loggedIn) { - // "The refresh method was not executed because the AuthenticationProvider is not logged in." - throw new Error(progress.data._getMsgText("jsdoMSG053", "AuthenticationProvider", "refresh")); - } - - if (!this.hasRefreshToken()) { - // "Token refresh was not executed because the AuthenticationProvider does not have a - // refresh token." - throw new Error(progress.data._getMsgText("jsdoMSG054", "AuthenticationProvider")); - } - - xhr = new XMLHttpRequest(); - openRefreshRequest(xhr); - - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - // process the response from the Web application - processRefreshResult(xhr, deferred); - } - }; - - xhr.send('{"token_type":"' + retrieveTokenType() + '","refresh_token":"' + - retrieveRefreshToken() + '"}'); - return deferred.promise(); - }; - - - this.hasRefreshToken = function () { - return (retrieveRefreshToken() === null ? false : true); - }; - - - // PROCESS CONSTRUCTOR ARGUMENTS, CREATE API PROPERTIES, ETC. - this._initialize(uri, - progress.data.Session.AUTH_TYPE_FORM_SSO, - {"_loginURI": progress.data.AuthenticationProvider._springFormTokenLoginURIBase, - "_logoutURI": progress.data.AuthenticationProvider._springLogoutURIBase, - "_refreshURI": progress.data.AuthenticationProvider._springFormTokenRefreshURIBase - }); - - // in addition to the standard AuthenticationProvider properties, an SSO provider also has a property - // to control automatic token refresh (it's enabled by default on the assumption that developers - // will usually want this) - _automaticTokenRefresh = true; - Object.defineProperty(this, 'automaticTokenRefresh', - { - get: function () { - return _automaticTokenRefresh; - }, - set: function (value) { - if (value === true || value === false) { - _automaticTokenRefresh = value; - } else { - throw new Error(progress.data._getMsgText("jsdoMSG061", - "AuthenticationProvider", - "automaticTokenRefresh")); - } - }, - enumerable: true - }); - - // add the automaticTokenRefresh key to the base class's list of data keys - this._dataKeys.automaticTokenRefresh = this._storageKey + ".automaticTokenRefresh"; - // set it from storage, if it's in storage - temp = this._retrieveInfoItem(this._dataKeys.automaticTokenRefresh); - if (temp === false) { - _automaticTokenRefresh = false; - } - - // We're currently storing the token in storage with the - // uri as the key. This is subject to change later. - tokenDataKeys.token = this._storageKey + tokenDataKeys.token; - tokenDataKeys.refreshToken = this._storageKey + tokenDataKeys.refreshToken; - tokenDataKeys.tokenType = this._storageKey + tokenDataKeys.tokenType; - tokenDataKeys.expiration = this._storageKey + tokenDataKeys.expiration; - tokenDataKeys.accessTokenExpiration = this._storageKey + tokenDataKeys.accessTokenExpiration; - - // NOTE: we rely on the prototype's logic to set this._loggedIn. An alternative could be to - // use the presence of a token to determine that, but it's conceivable that we could be - // logged in but for some reason not have a token (e.g., a token expired, or we logged in - // but the authentication server did not return a token) - if (retrieveToken()) { - this._loggedIn = true; - } - - // END OF CONSTRUCTOR PROCESSING - - }; - // END OF AuthenticationProviderSSO CONSTRUCTOR - - // NOTE: This is used only for the SSO authentication. - // Define the prototype as an instance of an AuthenticationProviderForm object - function SSOProxy() {} - SSOProxy.prototype = progress.data.AuthenticationProviderForm.prototype; - progress.data.AuthenticationProviderSSO.prototype = - new SSOProxy(); - - // But reset the constructor back to the SSO constructor (this is pretty much irrelevant, - // though. The correct constructor would be called anyway. It's mainly for the sake of anyone - // wanting to see what the constructor of an object is (maybe a framework) - progress.data.AuthenticationProviderSSO.prototype.constructor = - progress.data.AuthenticationProviderSSO; - - // override the base AuthenticationProvider _storeInfo and _clearinfo, but keep refs so they - // can be invoked within the overrides - fn = progress.data.AuthenticationProviderSSO.prototype._storeInfo; - progress.data.AuthenticationProviderSSO.prototype._storeInfo = - function () { - progress.data.AuthenticationProviderSSO.prototype._storeInfo._super.apply(this); - this._storage.setItem(this._dataKeys.automaticTokenRefresh, - JSON.stringify(this._automaticTokenRefresh)); - }; - progress.data.AuthenticationProviderSSO.prototype._storeInfo._super = fn; - - fn = progress.data.AuthenticationProviderSSO.prototype._clearInfo; - progress.data.AuthenticationProviderSSO.prototype._clearInfo = - function () { - progress.data.AuthenticationProviderSSO.prototype._clearInfo._super.apply(this); - this._storage.removeItem(this._dataKeys.automaticTokenRefresh); - }; - progress.data.AuthenticationProviderSSO.prototype._clearInfo._super = fn; - - - - // NOTE: There are no overrides of the following methods (either here or in the constructor). - // This object uses these methods from the original prototype(i.e., the implementations from the - // Auth...Form object) because for an OE SSO token server, the login/logout model is Form (the - // only difference is the use of a special URI query string in the login (see the call to - // initialize() in the SSO constructor (above)): - // login (API method) - // _openLoginRequest (API helper method) - // logout (API method) - // _openLogoutRequest (API helper method) - // _processLogoutResult (API helper method) - - // NOTE: All overrides are implemented in the constructor (rather than adding them to the prototype) - // because they need access to variables and/or functions that are defined in the constructor - // (in an attempt to protect the token info somewhat) - -}()); - - -/* -progress.data.kendo.js Version: 4.4.0-01 - -Copyright (c) 2015-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -/*global jQuery, kendo, progress*/ -/*jslint nomen: true*/ -/*jslint vars: false*/ -(function () { - - // "use strict"; - - var JSDODataReader, JSDOTransport, JSDOObservable = new kendo.Observable(); - - function initializeJSDO(transport, options) { - var jsdo, resourceName; - - if (options.jsdo instanceof progress.data.JSDO) { - jsdo = options.jsdo; - } else if (typeof (options.jsdo) === "string") { - // Create a new JSDO instance using the specified configuration - resourceName = options.jsdo; - - // Create JSDO - jsdo = new progress.data.JSDO({ name: resourceName }); - } else { - throw new Error("JSDO: jsdo property must be either a JSDO instance or a string."); - } - - if (transport.tableRef === undefined && jsdo._defaultTableRef) { - transport.tableRef = jsdo._defaultTableRef._name; - } - if (transport.tableRef === undefined) { - throw new Error("JSDO: A tableRef must be specified when using a multi-table DataSet."); - } else if (jsdo[transport.tableRef] === undefined) { - throw new Error("JSDO: tableRef '" + transport.tableRef + "' is not present in JSDO definition."); - } - - return jsdo; - } - - - - // This define functions to read the data object from the JSDO DataSource - JSDODataReader = kendo.data.readers.json.extend({ - init: function (arg1) { - // init function - var event = {}, - transport; - - // Query the transport object to obtain a transport reference - JSDOObservable.trigger("info", event); - transport = this.transport = event.sender._events.info.transport; - - kendo.data.readers.json.fn.init.call(this, arg1); - - // Overrides model property after init.call - // because if model is defined at the object level init.call removes it - this.model = kendo.data.Model.define({ - init: function (data) { - var record; - if (!data || jQuery.isEmptyObject(data)) { - data = transport._getInitialValues(); - } - record = transport._convertDataTypes(data); - transport.jsdo._deleteProdsProperties(record, true); - kendo.data.Model.fn.init.call(this, record); - }, - id: "_id", - fields: transport._getModel() - }); - }, - total: function (data) { - return data.total || (data.data ? data.data.length : data.length); - }, - data: function (data) { - return data.data || data; - } - }); - - // This define transport for JSDO DataSource providing implementation for: - // create, read, update, destroy, submit - JSDOTransport = kendo.data.RemoteTransport.extend({ - init: function (options) { - var transport = this, - fnName; - - if (options.tableRef !== undefined) { - transport.tableRef = options.tableRef; - } - transport.jsdo = initializeJSDO(transport, options); - transport._initFromServer = false; - transport.autoSave = options.autoSave !== undefined ? options.autoSave : true; - transport.readLocal = options.readLocal !== undefined ? options.readLocal : false; - transport.countFnName = options.countFnName; - transport.useArrays = options.useArrays !== undefined ? options.useArrays : false; - - if (transport.countFnName !== undefined) { - if (typeof (transport.jsdo[transport.countFnName]) !== "function") { - throw new Error("Invoke operation '" + - transport.countFnName + "' for countFnName is not defined."); - } - } else if (transport.jsdo._resource.generic.count !== undefined) { - for (fnName in transport.jsdo._resource.fn) { - if (transport.jsdo._resource.fn.hasOwnProperty(fnName)) { - if (transport.jsdo._resource.generic.count === transport.jsdo._resource.fn[fnName]["function"]) { - transport.countFnName = fnName; - break; - } - } - } - } - - // Define "info" event to return transport object to reader - JSDOObservable.one("info", function (e) { - e.sender._events.info.transport = transport; - }); - - transport._initConvertTypes(); - - kendo.data.RemoteTransport.fn.init.call(this, options); - }, - _initConvertTypes: function () { - // _initConvertTypes: - // Initializes transport._convertTypes to indicate whether a conversion of the data is needed - // when it is passed to Kendo UI. - // This operation is currently only needed for date fields that are stored as strings. - // Sets array _dateFields to the fields of date fields to convert. - var transport = this, - i, - schema, - fieldName, - dateFields = [], - arrayFields = [], - convertDateFields = false; - - transport._convertTypes = false; - - schema = transport.jsdo[transport.tableRef].getSchema(); - for (i = 0; i < schema.length; i += 1) { - fieldName = schema[i].name; - if (fieldName.length > 0 && fieldName.charAt(0) !== "_") { - if (schema[i].type === "string" && - schema[i].format && - (schema[i].format.indexOf("date") !== -1)) { - dateFields.push(fieldName); - if (!convertDateFields) { - convertDateFields = true; - } - } else if (!transport.useArrays && schema[i].type === "array") { - arrayFields.push(fieldName); - if (!convertDateFields && schema[i].ablType && - schema[i].ablType.indexOf("DATE") === 0) { - convertDateFields = true; - } - } - } - } - - if (dateFields.length > 0 || arrayFields.length > 0) { - transport._convertTypes = true; - // _convertFields: Object containing arrays for each data type to convert - transport._convertFields = {}; - transport._convertFields._arrayFields = []; - transport._convertFields._dateFields = []; - } - if (dateFields.length > 0) { - transport._convertFields._dateFields = dateFields; - } - if (convertDateFields) { - transport._convertFields._datePattern = new RegExp("^([0-9]+)?-([0-9]{2})?-([0-9]{2})?$"); - transport._convertFields._dateTimePattern = new RegExp( - "^([0-9]+)?-([0-9]{2})?-([0-9]{2})?" + - "T([0-9]{2})?:([0-9]{2})?:([0-9]{2})?.([0-9]{3})?$" - ); - } - if (arrayFields.length > 0) { - transport._convertFields._arrayFields = arrayFields; - } - }, - _convertStringToDate: function (data, fieldName, targetFieldName) { - var transport = this, - array, - ablType, - orig; - - if (!targetFieldName) { - targetFieldName = fieldName; - } - // Check if string is -- - array = transport._convertFields._datePattern.exec(data[targetFieldName]) || []; - if (array.length > 0) { - data[targetFieldName] = new Date(parseInt(array[1], 10), - parseInt(array[2], 10) - 1, - parseInt(array[3], 10)); - } else { - ablType = transport.jsdo[transport.tableRef]._fields[fieldName.toLowerCase()].ablType; - if (ablType === "DATETIME") { - array = transport._convertFields._dateTimePattern.exec(data[targetFieldName]) || []; - if (array.length > 0) { - // Convert date to local time zone - data[targetFieldName] = new Date(parseInt(array[1], 10), - parseInt(array[2], 10) - 1, - parseInt(array[3], 10), - parseInt(array[4], 10), - parseInt(array[5], 10), - parseInt(array[6], 10), - parseInt(array[7], 10)); - } - } - - // Check to see if it was converted - if (typeof (data[targetFieldName]) === "string") { - orig = data[targetFieldName]; - try { - data[targetFieldName] = new Date(data[targetFieldName]); - } - catch (e) { - // Conversion to a date object was not successful - data[targetFieldName] = orig; - console.log(msg.getMsgText("jsdoMSG000", - "_convertStringToDate() could not convert to date object: " + orig)); - } - } - } - }, - _convertDataTypes: function (data) { - // _convertDataTypes: - // Converts data types in the specified data record. - // Data record could come from the JSDO or from the Kendo UI DataSource. - // Returns a reference to the record. - // Returns a copy when useArrays is undefined or false. - var transport = this, - i, - k, - fieldName, - schemaInfo, - prefixElement, - elementName, - copy; - - if (!transport.useArrays && transport._convertTypes && (transport._convertFields._arrayFields.length > 0)) { - copy = {}; - transport.jsdo._copyRecord(transport.jsdo._buffers[transport.tableRef], data, copy); - data = copy; - } - - if (!transport._convertTypes) { - return data; - } - - for (k = 0; k < transport._convertFields._arrayFields.length; k += 1) { - fieldName = transport._convertFields._arrayFields[k]; - if (data[fieldName]) { - schemaInfo = transport.jsdo[transport.tableRef]._fields[fieldName.toLowerCase()]; - prefixElement = transport.jsdo._getArrayField(fieldName); - for (i = 0; i < schemaInfo.maxItems; i += 1) { - // ABL arrays are 1-based - elementName = prefixElement.name + (i + 1); - - if (!transport.jsdo[transport.tableRef]._fields[elementName.toLowerCase()]) { - // Skip element if a field with the same name exists - // Extract value from array field into individual field - // Array is removed later - data[elementName] = data[fieldName][i]; - - // Convert string DATE fields to JS DATE - if ((schemaInfo.ablType) && (schemaInfo.ablType.indexOf("DATE") === 0) && (typeof (data[elementName]) === "string")) { - transport._convertStringToDate(data, fieldName, elementName); - } - } - } - if (!transport.useArrays) { - delete data[fieldName]; - } - } - } - - for (k = 0; k < transport._convertFields._dateFields.length; k += 1) { - fieldName = transport._convertFields._dateFields[k]; - if (typeof (data[fieldName]) === "string") { - transport._convertStringToDate(data, fieldName); - } - } - - return data; - }, - _getModel: function () { - var transport = this, - i, - j, - fields = {}, - schema, - value, - type, - element; - - schema = transport.jsdo[transport.tableRef].getSchema(); - for (i = 0; i < schema.length; i += 1) { - // Skip internal fields - if (schema[i].name.length > 0 && schema[i].name.charAt(0) !== "_") { - type = schema[i].type; - if (type === "integer") { - type = "number"; - } else if (type === "string" && - schema[i].format && - schema[i].format.indexOf("date") !== -1) { - // Set type to "date" is type is DATE or DATETIME[-TZ] - type = "date"; - } - if (type === "array") { - for (j = 0; j < schema[i].maxItems; j += 1) { - value = transport.jsdo._getDefaultValue(schema[i]); - element = transport.jsdo._getArrayField(schema[i].name, j); - if (!transport.jsdo[transport.tableRef]._fields[element.name.toLowerCase()]) { - // Skip element if a field with the same name exists - - // Calculate type of array element - type = schema[i].items.type; - if (type === "integer") { - type = "number"; - } else if (type === "string" && schema[i].ablType && (schema[i].ablType.indexOf("DATE") !== -1)) { - type = "date"; - } - - fields[element.name] = {}; - fields[element.name].type = type; - if (value !== undefined) { - fields[element.name].defaultValue = value; - } - } - } - } else { - value = transport.jsdo._getDefaultValue(schema[i]); - fields[schema[i].name] = {}; - fields[schema[i].name].type = type; - if (value !== undefined) { - fields[schema[i].name].defaultValue = value; - } - } - } - } - return fields; - }, - _getInitialValues: function () { - var transport = this, - i, - j, - data = {}, - schema, - defaultValue; - schema = transport.jsdo[transport.tableRef].getSchema(); - for (i = 0; i < schema.length; i += 1) { - // Skip internal fields - if (schema[i].name.length > 0 && schema[i].name.charAt(0) !== "_") { - defaultValue = transport.jsdo._getDefaultValue(schema[i]); - if (schema[i].type === "array") { - data[schema[i].name] = []; - - for (j = 0; j < schema[i].maxItems; j += 1) { - data[schema[i].name][j] = defaultValue; - } - - } else { - data[schema[i].name] = defaultValue; - } - } - } - return data; - }, - _getData: function (options) { - var jsdo = this.jsdo, - data = {}, - params, - array, - filter; - - if (options && options.data) { - params = { - tableRef: this.tableRef, - filter: options.data.filter, - sort: options.data.sort, - skip: options.data.skip, - top: options.data.take - }; - - array = jsdo[this.tableRef].getData({filter: filter}); - data.total = array.length; - array = jsdo[this.tableRef].getData(params); - data.data = array; - } else { - array = jsdo[this.tableRef].getData(); - data.data = array; - data.total = array.length; - } - - return data; - }, - read: function (options) { - try { - var jsdo = this.jsdo, - filter, - data = {}, - transport = this, - callback, - property, - optionsMapping = { - filter: "filter", - take: "top", - skip: "skip", - sort: "sort" - }, - saveUseRelationships; - - if (!this._initFromServer) { - if (jsdo[this.tableRef]._parent) { - this._initFromServer = (jsdo[jsdo[this.tableRef]._parent]._data && (jsdo[jsdo[this.tableRef]._parent]._data.length > 0)) - || (jsdo[this.tableRef]._data instanceof Array && (jsdo[this.tableRef]._data.length > 0)); - } else { - this._initFromServer = (jsdo[this.tableRef]._data instanceof Array) && (jsdo[this.tableRef]._data.length > 0); - } - } - - data.data = []; - if (this.readLocal && this._initFromServer) { - saveUseRelationships = jsdo.useRelationships; - jsdo.useRelationships = false; - data = this._getData(options); - jsdo.useRelationships = saveUseRelationships; - options.success(data); - return; - } - - if (!this.readLocal) { - // readLocal is false or _initFromServer is false - - if (options.data) { - // Only create filter object if options.data contains viable properties - for (property in options.data) { - if (options.data.hasOwnProperty(property)) { - if (options.data[property] !== undefined - && optionsMapping[property]) { - if (filter === undefined) { - filter = {}; - } - filter[optionsMapping[property]] = options.data[property]; - } - } - } - if (filter) { - filter.tableRef = this.tableRef; - } - } - } - - callback = function onAfterFillJSDO(jsdo, success, request) { - var data = {}, saveUseRelationships, promise, total, exception; - - if (success) { - saveUseRelationships = jsdo.useRelationships; - jsdo.useRelationships = false; - - if (transport.readLocal) { - // Use options.data to filter data - data = transport._getData(options); - } else { - data.data = jsdo[transport.tableRef].getData(); - - total = jsdo.getProperty("server.count"); - if (total) { - data.total = total; - } - - } - jsdo.useRelationships = saveUseRelationships; - transport._initFromServer = true; - if (options.data && options.data.take) { - if (!transport.readLocal && - transport.countFnName !== undefined && - typeof (jsdo[transport.countFnName]) === "function") { - - if (options.data.skip === 0 && options.data.take > data.data.length) { - options.success(data); - return; - } - - // Reuse filter string from the request.objParam object from fill() call. - promise = jsdo.invoke( - transport.countFnName, - { filter: request.objParam.filter } - ); - /*jslint unparam: true*/ - promise.done(function (jsdo, success, request) { - var exception, total; - - try { - if (typeof (request.response) === "object" && - Object.keys(request.response).length === 1) { - total = request.response[Object.keys(request.response)]; - if (typeof (total) !== "number") { - // Use generic exception if data type is not a number. - total = undefined; - } - } - } catch (e) { - // This exception is ignored a generic exception is used later. - } - if (total !== undefined) { - if (total) { - data.total = total; - } - options.success(data); - } else { - exception = new Error("Unexpected response from '" - + transport.countFnName + "' operation."); - options.error(request.xhr, request.xhr.status, exception); - } - }); - promise.fail(function (jsdo, success, request) { - var exception; - exception = new Error("Error invoking '" - + transport.countFnName + "' operation."); - options.error(request.xhr, request.xhr.status, exception); - }); - /*jslint unparam: false*/ - } else { - options.success(data); - } - } else { - options.success(data); - } - } else { - exception = new Error("Error while reading records."); - options.error(request.xhr, request.xhr.status, exception); - } - }; - - jsdo.fill(filter).done(callback).fail(callback); - - } catch (e) { - options.error(null, null, e); - } - }, - _processChanges: function (options, request) { - var jsdo = this.jsdo, - transport = this, - array, - i, - jsrecord, - id, - record; - - if (options.batch) { - array = []; - if (options.data.created instanceof Array) { - for (i = 0; i < options.data.created.length; i += 1) { - jsrecord = jsdo[transport.tableRef].findById( - options.data.created[i]._id - ); - if (jsrecord) { - record = transport._convertDataTypes(jsrecord.data); - array.push(record); - } else if (jsdo.autoApplyChanges) { - options.error( - null, - null, - new Error("Created record was not found in memory.") - ); - return; - } - } - } - options.success(array, "create"); - - array = []; - if (options.data.updated instanceof Array) { - for (i = 0; i < options.data.updated.length; i += 1) { - jsrecord = jsdo[transport.tableRef].findById( - options.data.updated[i]._id - ); - if (jsrecord) { - record = transport._convertDataTypes(jsrecord.data); - array.push(record); - } else if (jsdo.autoApplyChanges) { - options.error( - null, - null, - new Error("Updated record not found in memory.") - ); - return; - } - } - } - options.success(array, "update"); - - array = []; - if (options.data.destroyed instanceof Array) { - for (i = 0; i < options.data.destroyed.length; i += 1) { - jsrecord = jsdo[transport.tableRef].findById( - options.data.destroyed[i]._id - ); - if (jsrecord && jsdo.autoApplyChanges) { - options.error( - null, - null, - new Error("Deleted record was found in memory.") - ); - return; - } - } - } - options.success(array, "destroy"); - } else { - if (jsdo._resource.idProperty) { - if (request - && request.batch - && request.batch.operations instanceof Array - && request.batch.operations.length === 1) { - id = request.batch.operations[0].jsrecord.data._id; - } - } else { - id = options.data._id; - } - jsrecord = jsdo[transport.tableRef].findById(id); - if (jsrecord) { - record = transport._convertDataTypes(jsrecord.data); - options.success(record); - } else { - options.success({}); - } - } - }, - _saveChanges: function (options) { - var transport = this, - callback = function onAfterSaveChanges(jsdo, success, request) { - var jsrecord, - xhr, - status, - exception; - - if (success) { - // _id is expected to be set for CUD operations - // Deleted records will not be found and data is expected to be undefined - transport._processChanges(options, request); - } else { - if (request.batch - && request.batch.operations instanceof Array - && request.batch.operations.length === 1) { - xhr = request.batch.operations[0].xhr; - status = request.batch.operations[0].xhr.status; - } else if (request.jsrecords) { - xhr = request.xhr; - status = request.xhr.status; - } - exception = new Error("Error while saving changes."); - options.error(xhr, status, exception); - } - }; - - if (this.autoSave) { - this.jsdo.saveChanges(this.jsdo._hasSubmitOperation).done(callback).fail(callback); - } else { - this._processChanges(options); - } - }, - create: function (options) { - var jsdo = this.jsdo, - jsrecord, - saveUseRelationships = jsdo.useRelationships; - - options.batch = options.data.models instanceof Array; - try { - jsdo.useRelationships = false; - if (options.batch) { - options.error( - null, - null, - new Error("A newer version of Kendo UI is expected for batching support.") - ); - } else { - jsrecord = jsdo[this.tableRef].add(options.data); - options.data._id = jsrecord.data._id; - this._saveChanges(options); - } - } catch (e) { - // Undo changes on thrown exception - if (jsdo.autoApplyChanges) { - jsdo[this.tableRef].rejectChanges(); - } - options.error(null, null, e); - } finally { - jsdo.useRelationships = saveUseRelationships; - } - }, - update: function (options) { - var jsdo = this.jsdo, - jsrecord, - saveUseRelationships = jsdo.useRelationships; - - options.batch = options.data.models instanceof Array; - try { - jsdo.useRelationships = false; - if (options.batch) { - options.error( - null, - null, - new Error("A newer version of Kendo UI is expected for batching support.") - ); - } else { - jsrecord = jsdo[this.tableRef].findById(options.data._id); - jsrecord.assign(options.data); - this._saveChanges(options); - } - } catch (e) { - // Undo changes on thrown exception - if (jsdo.autoApplyChanges) { - jsdo[this.tableRef].rejectChanges(); - } - options.error(null, null, e); - } finally { - jsdo.useRelationships = saveUseRelationships; - } - }, - destroy: function (options) { - var jsdo = this.jsdo, - jsrecord, - saveUseRelationships = jsdo.useRelationships; - - options.batch = options.data.models instanceof Array; - try { - jsdo.useRelationships = false; - if (options.data.models instanceof Array) { - options.error( - null, - null, - new Error("A newer version of Kendo UI is expected for batching support.") - ); - } else { - jsrecord = jsdo[this.tableRef].findById(options.data._id); - jsrecord.remove(); - this._saveChanges(options); - } - } catch (e) { - // Undo changes on thrown exception - if (jsdo.autoApplyChanges) { - jsdo[this.tableRef].rejectChanges(); - } - options.error(null, null, e); - } finally { - jsdo.useRelationships = saveUseRelationships; - } - }, - submit: function (options) { - var jsdo = this.jsdo, - i, - jsrecord, - saveUseRelationships = jsdo.useRelationships; - - options.batch = true; - try { - jsdo.useRelationships = false; - - if (options.data.created instanceof Array) { - for (i = 0; i < options.data.created.length; i += 1) { - jsrecord = jsdo[this.tableRef].add(options.data.created[i]); - options.data.created[i]._id = jsrecord.data._id; - } - } - - if (options.data.updated instanceof Array) { - for (i = 0; i < options.data.updated.length; i += 1) { - jsrecord = jsdo[this.tableRef].findById(options.data.updated[i]._id); - if (jsrecord) { - jsrecord.assign(options.data.updated[i]); - } else { - options.error(null, null, new Error("Record not found in memory.")); - } - } - } - - if (options.data.destroyed instanceof Array) { - for (i = 0; i < options.data.destroyed.length; i += 1) { - jsrecord = jsdo[this.tableRef].findById(options.data.destroyed[i]._id); - if (jsrecord) { - jsrecord.remove(); - } else { - options.error(null, null, new Error("Record not found in memory.")); - } - } - } - - this._saveChanges(options); - } catch (e) { - // Undo changes on thrown exception - if (jsdo.autoApplyChanges) { - jsdo[this.tableRef].rejectChanges(); - } - options.error(null, null, e); - } finally { - jsdo.useRelationships = saveUseRelationships; - } - } - }); - - // This defines the JSDO DataSource by specifying the schema, transport and reader for it. - // The "id" property is set to "_id" to enable CUD operations. - jQuery.extend(true, kendo.data, { - schemas: { - jsdo: { - type: "jsdo", - model: { - id: "_id" - } - } - }, - transports: { - jsdo: JSDOTransport - }, - readers: { - jsdo: JSDODataReader - } - }); -}()); -/*jslint nomen: false*/ From 760ff3732d2040d0e6a21b05d62f2a3a814af508 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 7 Sep 2023 18:30:02 +0530 Subject: [PATCH 017/135] deploy script changes --- deploy/deploy.sh | 3 ++- deploy/docker-compose.yml | 2 +- deploy/undeploy.sh | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 41bee6e..877c54f 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -1,8 +1,9 @@ #!/bin/bash -# create the app image +# create the app docker image docker build --no-cache -t sports:latest . +# deploy PAS_INSTANCE_NAME=oepas1 docker-compose -p ${PAS_INSTANCE_NAME} up -d echo "PASOE instance named '${PAS_INSTANCE_NAME}_dc' will be available at 'https://localhost:8811'" \ No newline at end of file diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 9478212..bc8eee6 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -48,7 +48,7 @@ services: volume: nocopy: true - ./license/progress.cfg:/psc/dlc/progress.cfg - - ./conf/runtime.properties:/deploy/scripts/config/runtime.properties + # - ./conf/runtime.properties:/deploy/scripts/config/runtime.properties - ./conf/logging:/fluentbit-tlr - ./scripts/startServer.sh:/deploy/scripts/startServer.sh volumes: diff --git a/deploy/undeploy.sh b/deploy/undeploy.sh index f3fdf60..4136638 100644 --- a/deploy/undeploy.sh +++ b/deploy/undeploy.sh @@ -1,5 +1,6 @@ #!/bin/bash +# undeploy PAS_INSTANCE_NAME=oepas1 docker-compose -p ${PAS_INSTANCE_NAME} down -v echo "Removed '${PAS_INSTANCE_NAME}_dc'" \ No newline at end of file From b342d13b524bc68127bc467bc2d041910707450c Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 7 Sep 2023 19:30:39 +0530 Subject: [PATCH 018/135] refactor sports app --- SportsApp/NOTICE.txt | 38 --- SportsApp/README.md | 88 ------- SportsApp/Sports/build.gradle | 2 +- SportsApp/build.gradle | 31 +++ SportsApp/config.properties | 22 -- {deploy => SportsApp/deploy}/Dockerfile | 0 {deploy => SportsApp/deploy}/deploy.sh | 0 .../deploy}/docker-compose.yml | 4 +- .../deploy}/scripts/startServer.sh | 0 {deploy => SportsApp/deploy}/undeploy.sh | 0 SportsApp/docker-compose.yaml | 23 -- .../fluentbit/conf/fluent-bit-output.conf | 10 - SportsApp/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59536 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + SportsApp/gradlew | 234 ++++++++++++++++++ SportsApp/gradlew.bat | 89 +++++++ SportsApp/settings.gradle | 11 + deploy/.gitignore | 2 - deploy/ablapps/.gitignore | 4 - deploy/license/.gitignore | 4 - 20 files changed, 373 insertions(+), 194 deletions(-) delete mode 100644 SportsApp/NOTICE.txt delete mode 100644 SportsApp/README.md create mode 100644 SportsApp/build.gradle delete mode 100644 SportsApp/config.properties rename {deploy => SportsApp/deploy}/Dockerfile (100%) rename {deploy => SportsApp/deploy}/deploy.sh (100%) rename {deploy => SportsApp/deploy}/docker-compose.yml (93%) rename {deploy => SportsApp/deploy}/scripts/startServer.sh (100%) rename {deploy => SportsApp/deploy}/undeploy.sh (100%) delete mode 100644 SportsApp/docker-compose.yaml delete mode 100644 SportsApp/fluentbit/conf/fluent-bit-output.conf create mode 100644 SportsApp/gradle/wrapper/gradle-wrapper.jar create mode 100644 SportsApp/gradle/wrapper/gradle-wrapper.properties create mode 100644 SportsApp/gradlew create mode 100644 SportsApp/gradlew.bat create mode 100644 SportsApp/settings.gradle delete mode 100644 deploy/.gitignore delete mode 100644 deploy/ablapps/.gitignore delete mode 100644 deploy/license/.gitignore diff --git a/SportsApp/NOTICE.txt b/SportsApp/NOTICE.txt deleted file mode 100644 index 0d2a52e..0000000 --- a/SportsApp/NOTICE.txt +++ /dev/null @@ -1,38 +0,0 @@ -PASOE-Basic Sample Code v12 - -Copyright © 2018-2020 Progress Software Corporation and/or one of its subsidiaries or affiliates. All rights reserved. - -Portions of the Product include certain open source and commercial third-party components listed below (“Third-Party Components. The authors of the Third-Party Components require Progress Software Corporation (“PSC”) to include the following notices and additional licensing terms as a condition of PSC’s use of such Third-Party Components. You acknowledge that the authors of the Third-Party Components have no obligation to provide support to you for the Third-Party Components or the Product. You hereby undertake to comply with all licenses related to the applicable Third-Party Components. Notwithstanding anything to the contrary, to the extent that any of the terms and conditions of the Progress Agreement conflict, vary, or are in addition to the terms and conditions of the aforementioned third-party licenses for these technologies, such terms and conditions are offered by PSC alone and not by any other party. - -1. Special Notices Regarding Open Source Third-Party Components incorporated in the Product: - -(1) MIT-style Licenses: - -(a) PASOE-Basic Sample Code v12 incorporates chai v4.1.2. Such technology is subject to the following terms and conditions: -MIT License -Copyright (c) 2017 Chai.js Assertion Library -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -(b) PASOE-Basic Sample Code v12 incorporates mocha v5.2.0. Such technology is subject to the following terms and conditions: -(The MIT License) -Copyright (c) 2011-2018 JS Foundation and contributors, https://js.foundation -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -(c) PASOE-Basic Sample Code v12 incorporates mocha-teamcity-reporter v2.4.0. Such technology is subject to the following terms and conditions: -The MIT License (MIT) -Copyright (c) 2016 Jamie Sherriff -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -2. Special Notices Regarding Commercially Licensed Third-Party Components incorporated in the Product: None - - -NOTICE FROM PROGRESS SOFTWARE CORPORATION: Additional notices may be included in the release notes or other documentation that accompanies updates received in connection with support of the Product. - - -Updated 2/17/2020 diff --git a/SportsApp/README.md b/SportsApp/README.md deleted file mode 100644 index 1fd7e5f..0000000 --- a/SportsApp/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# Use the Container Image for PAS for OpenEdge 12.6.0 with a Sample Application - -## Requirements: -* Docker environment -* Docker Compose -* A valid progress.cfg file -* OpenEdge 12.6.0 Environment -* Scripts to deploy Progress Application Server for OpenEdge Container. This can be obtained either from the Progress Download Center or Progress communities. - -Note: For the deployment, we are using service port 8811(sample-app), 9200(elasticsearch), 5601(kibana) and 8080(web-ui). These ports should be available. - -## Use the Container Image - -1. Start a database server if you don't have a running database server. Below is an example - * Create a copy of the sports2000 database and start the database server (broker) - * `prodb sports sports2000` - * `proserve sports -S ` - -2. Build the Sports sample app: - * cd Sports - * For connecting to the database, update the ./conf/startup.pf file with the below content and substituting the required field. - * `-db sports -H -S ` - * In an openedge environment, run the below command - * `ant package` - * Note: A Sports.zip file is generated in ./output/package-output - * Change the working directory to the parent to follow further steps - * `cd ..` - -3. Start the Elasticsearch, Kibana and web-ui service - * Update the value for `serviceURI` in `webui/grid.js` to point to your Docker host. - * Increase virtual memory settings to run Elasticsearch: - * `sudo sysctl -w vm.max_map_count=262144` - * For additional info : https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html - * Start the services - * `docker-compose up -d` - * Check the status of Elasticsearch via a web browser. Elasticsearch may take some time to start. - * `http://:9200` - * Check the Elasticsearch starts: - * `docker-compose logs -f elasticsearch` - -4. Deploy the sample app in PASOE - * Download the PASOE image zip from Electronic Software Distribution (ESD) and unzip it to a folder say `pasoe-sample-app`. - For more details, refer https://docs.progress.com/bundle/pas-for-openedge-docker/ - * Change the directory - * `cd pasoe-sample-app` - * Copy the Sports.zip generated from Step 2 to `./deploy/ablapps` - * `cp ../Sports/output/package-output/Sports.zip ./deploy/ablapps/` - * Copy the config.properties from sample app to `./deploy/config.properties` - * `cp ../config.properties ./deploy/config.properties` - * Update the value for `HOST` in `../fluentbit/conf/fluent-bit-output.conf` to point to your Elasticsearch host. - * Copy the Fluent Bit config from sample app to push the logs to Elasticsearch - * `cp ../fluentbit/conf/fluent-bit-output.conf ./deploy/conf/logging/` - * Copy the license file `progress.cfg` to `./deploy/license` - * If you want to change the database during deployment, you can do this by updating the ./deploy/conf/runtime.properties with below content and substituting the requied field. If `localhost` is used in Step 2 for connecting to the database, please do update the ./deploy/conf/runtime.properties to point to the IP_ADDRESS_OF_DB_HOST. - * `Sports.DB.CONNECTION.PARAMS=-db sports -H -S ` - * Deploy the sample app - * `ant -f ./deploy/build.xml deploy` -5. Access the PAS for OpenEdge instance via a web browser: - * `https://:8811/` - * `https://:8811/Sports/` - * `https://:8811/Sports/static/SportsService.json` - * `https://:8811/Sports/rest/SportsService/Customer` - * Note: By default, the PAS for OpenEdge instance will use HTTPS with a test certificate. You will need to accept access with this certificate. - -6. Access the web-ui service via a web browser: - * `http://:8080` - -7. Access Elasticsearch to check on available logs - * `http://:9200/_cat/indices` - -8. Access Kibana - * `http://:5601` - * Notes: - * Select Management/Index Management to see the indices in Elasticsearch.A index named as `pasoe-container-logs` should be present. - * Select Management/Index Pattern to create an index for Kibana: - * Create index patterns as 'pasoe_container*' - * Specify @timestamp to filter data by time - * Select Discover to see pasoe logs. You can search logs for logtype: - * pasoe_agent_log, pasoe_application_log, pasoe_localhost_log, pasoe_localhost_access_log, start_server_log and etc. - -9. Stop the running services - * Stop Elasticsearch, Kibana, and web-ui - * `docker-compose -f ../docker-compose.yaml down` - * Stop deployed sample app - * `ant -f ./deploy/build.xml undeploy` - - - diff --git a/SportsApp/Sports/build.gradle b/SportsApp/Sports/build.gradle index 02d1ed9..649ccd9 100644 --- a/SportsApp/Sports/build.gradle +++ b/SportsApp/Sports/build.gradle @@ -29,7 +29,7 @@ task compileWebApp(type: ABLCompile){ task packageWebApp(type: OEWar){ webAppName = "Sports" - projectLocation = project.rootDir + projectLocation = project.projectDir println "projectLocation: ${projectLocation.get()}" verbose = true destinationDirectory = project.distsDirectory diff --git a/SportsApp/build.gradle b/SportsApp/build.gradle new file mode 100644 index 0000000..5811738 --- /dev/null +++ b/SportsApp/build.gradle @@ -0,0 +1,31 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This is a general purpose Gradle build. + * Learn more about Gradle by exploring our samples at https://docs.gradle.org/7.3.3/samples + */ +plugins { + id("base") +} + +task packageDeploy(type: Tar) { + archiveFileName = "sportsApp.tar.gz" + destinationDirectory = project.distsDirectory + compression = Compression.GZIP + from ("deploy"){ + include "scripts/**" + include "*.sh" + include "*.yml" + include "Dockerfile" + exclude "license" + into "deploy" + } + from ("webui"){ + into "webui" + } + from ("Sports/build/distributions/Sports.oear"){ + into "deploy/ablapps" + } +} +packageDeploy.dependsOn ":Sports:build" +build.dependsOn packageDeploy \ No newline at end of file diff --git a/SportsApp/config.properties b/SportsApp/config.properties deleted file mode 100644 index 91fa336..0000000 --- a/SportsApp/config.properties +++ /dev/null @@ -1,22 +0,0 @@ -# Deployment mode can be one of: docker/docker-compose/minikube -DEPLOYMENT.MODE=docker-compose - -# Name and tag with which app container image will be built -# Same name will be used as APP_NAME for fluentbit logging -APP.DOCKER.IMAGE.NAME=sports -APP.DOCKER.IMAGE.TAG=latest - -# Container image which contains JDK(compatible) in it -JDK.DOCKER.IMAGE.NAME=eclipse-temurin -JDK.DOCKER.IMAGE.TAG=17.0.3_7-jdk-centos7 -# Location/Path to JDK inside container -JDK.DOCKER.IMAGE.JAVA.LOCATION=/opt/java/openjdk - -PAS.INSTANCE.NAME=oepas1 -PASOE.DOCKER.IMAGE.NAME=progresssoftware/prgs-pasoe -PASOE.DOCKER.IMAGE.TAG=12.8.0 -# In case of kubernetes provide port should be in the default nodePort range: 30000-32767 -PASOE.HTTPS.PORT=8811 - -# Flag to enable fluent-bit logging, defaults to 'true' -FLUENTBIT.LOGGING=true diff --git a/deploy/Dockerfile b/SportsApp/deploy/Dockerfile similarity index 100% rename from deploy/Dockerfile rename to SportsApp/deploy/Dockerfile diff --git a/deploy/deploy.sh b/SportsApp/deploy/deploy.sh similarity index 100% rename from deploy/deploy.sh rename to SportsApp/deploy/deploy.sh diff --git a/deploy/docker-compose.yml b/SportsApp/deploy/docker-compose.yml similarity index 93% rename from deploy/docker-compose.yml rename to SportsApp/deploy/docker-compose.yml index bc8eee6..8f497ed 100644 --- a/deploy/docker-compose.yml +++ b/SportsApp/deploy/docker-compose.yml @@ -14,7 +14,7 @@ services: ports: - "8080:80" volumes: - - ./../SportsApp/webui:/usr/share/nginx/html:ro + - ./../webui:/usr/share/nginx/html:ro jdk: image: eclipse-temurin:17.0.3_7-jdk-centos7 volumes: @@ -49,7 +49,7 @@ services: nocopy: true - ./license/progress.cfg:/psc/dlc/progress.cfg # - ./conf/runtime.properties:/deploy/scripts/config/runtime.properties - - ./conf/logging:/fluentbit-tlr + # - ./conf/logging:/fluentbit-tlr - ./scripts/startServer.sh:/deploy/scripts/startServer.sh volumes: jdk_dc: diff --git a/deploy/scripts/startServer.sh b/SportsApp/deploy/scripts/startServer.sh similarity index 100% rename from deploy/scripts/startServer.sh rename to SportsApp/deploy/scripts/startServer.sh diff --git a/deploy/undeploy.sh b/SportsApp/deploy/undeploy.sh similarity index 100% rename from deploy/undeploy.sh rename to SportsApp/deploy/undeploy.sh diff --git a/SportsApp/docker-compose.yaml b/SportsApp/docker-compose.yaml deleted file mode 100644 index 7229162..0000000 --- a/SportsApp/docker-compose.yaml +++ /dev/null @@ -1,23 +0,0 @@ -version: '3' -services: - web: - image: nginx - ports: - - "8080:80" - volumes: - - ./webui:/usr/share/nginx/html:ro - elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:6.4.2 - expose: - - 9200 - ports: - - "9200:9200" -# https://www.elastic.co/guide/en/elasticsearch/reference/6.4/docker.html -# volumes: -# - ./esdata1:/usr/share/elasticsearch/data - kibana: - image: docker.elastic.co/kibana/kibana:6.4.2 - links: - - "elasticsearch" - ports: - - "5601:5601" diff --git a/SportsApp/fluentbit/conf/fluent-bit-output.conf b/SportsApp/fluentbit/conf/fluent-bit-output.conf deleted file mode 100644 index caf5bc6..0000000 --- a/SportsApp/fluentbit/conf/fluent-bit-output.conf +++ /dev/null @@ -1,10 +0,0 @@ -# OUTPUT -# ===== -# Define Output configurations in this file -[OUTPUT] - Name es - Match * - Host - Port 9200 - Index pasoe-container-logs - Type typename \ No newline at end of file diff --git a/SportsApp/gradle/wrapper/gradle-wrapper.jar b/SportsApp/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7454180f2ae8848c63b8b4dea2cb829da983f2fa GIT binary patch literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL literal 0 HcmV?d00001 diff --git a/SportsApp/gradle/wrapper/gradle-wrapper.properties b/SportsApp/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2e6e589 --- /dev/null +++ b/SportsApp/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/SportsApp/gradlew b/SportsApp/gradlew new file mode 100644 index 0000000..c53aefa --- /dev/null +++ b/SportsApp/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions $var, ${var}, ${var:-default}, ${var+SET}, +# ${var#prefix}, ${var%suffix}, and $( cmd ); +# * compound commands having a testable exit status, especially case; +# * various built-in commands including command, set, and ulimit. +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/SportsApp/gradlew.bat b/SportsApp/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/SportsApp/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/SportsApp/settings.gradle b/SportsApp/settings.gradle new file mode 100644 index 0000000..16a4617 --- /dev/null +++ b/SportsApp/settings.gradle @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.3.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'OpenEdgeOps' +include "Sports" \ No newline at end of file diff --git a/deploy/.gitignore b/deploy/.gitignore deleted file mode 100644 index 5629c95..0000000 --- a/deploy/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -output -webui \ No newline at end of file diff --git a/deploy/ablapps/.gitignore b/deploy/ablapps/.gitignore deleted file mode 100644 index 86d0cb2..0000000 --- a/deploy/ablapps/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file diff --git a/deploy/license/.gitignore b/deploy/license/.gitignore deleted file mode 100644 index 86d0cb2..0000000 --- a/deploy/license/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file From 2d1102c7c5133c6c471fdfa41e2230cab5ca992d Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 7 Sep 2023 19:30:59 +0530 Subject: [PATCH 019/135] update CI --- .github/workflows/github-actions-demo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 067599b..9e124c3 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -10,7 +10,7 @@ jobs: - run: sh gradlew clean build working-directory: ./SportsApp/Sports - name: upload zip file to nexus - run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/Sports.oear https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.oear + run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz working-directory: ./SportsApp/Sports From 87caccfbee9c336819c150177972d655f6f1eb15 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 7 Sep 2023 19:34:02 +0530 Subject: [PATCH 020/135] update CI --- .github/workflows/github-actions-demo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 9e124c3..bda0af1 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -8,10 +8,10 @@ jobs: steps: - uses: actions/checkout@v2 - run: sh gradlew clean build - working-directory: ./SportsApp/Sports + working-directory: ./SportsApp - name: upload zip file to nexus run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz - working-directory: ./SportsApp/Sports + working-directory: ./SportsApp From f91cc23e805fb33bf317504b8c453ea2293ce490 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Thu, 7 Sep 2023 10:23:55 -0400 Subject: [PATCH 021/135] added deploy after compile --- .github/workflows/github-actions-demo.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index bda0af1..31dd5f3 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -12,6 +12,16 @@ jobs: - name: upload zip file to nexus run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz working-directory: ./SportsApp + deploy: + name: Sample App Deploy + runs-on: self-hosted + steps: + - name: Download the application artifact + run: wget https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz --no-check-certificate + - name: Extract the artifact + run: tar -zx sportsapp-1.0.0.tar.gz + - name: Download the OpenEdge License file + run: wget -cO - https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > progress.cfg --no-check-certificate From 58ac92ebb2acf579bec86195a0b443011105fd50 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Thu, 7 Sep 2023 10:27:20 -0400 Subject: [PATCH 022/135] debug --- .github/workflows/github-actions-demo.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 31dd5f3..dc3bd32 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -16,6 +16,7 @@ jobs: name: Sample App Deploy runs-on: self-hosted steps: + - run: pwd - name: Download the application artifact run: wget https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz --no-check-certificate - name: Extract the artifact From 434d89b1788478bce2f966f5267050242f072779 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Thu, 7 Sep 2023 10:30:18 -0400 Subject: [PATCH 023/135] debug --- .github/workflows/github-actions-demo.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index dc3bd32..950480a 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -16,11 +16,10 @@ jobs: name: Sample App Deploy runs-on: self-hosted steps: - - run: pwd - name: Download the application artifact - run: wget https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz --no-check-certificate + run: pwd && wget https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz --no-check-certificate - name: Extract the artifact - run: tar -zx sportsapp-1.0.0.tar.gz + run: tar -zxf sportsapp-1.0.0.tar.gz - name: Download the OpenEdge License file run: wget -cO - https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > progress.cfg --no-check-certificate From 442b1b5a0a1628b0f2e6963cb5d5cc71d9e88fe4 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Thu, 7 Sep 2023 10:38:20 -0400 Subject: [PATCH 024/135] debug --- .github/workflows/github-actions-demo.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 950480a..55d8cdb 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -6,6 +6,8 @@ jobs: name: OpenEdge Compile job runs-on: self-hosted steps: + - name: Clean-up + run: rm -Rf * - uses: actions/checkout@v2 - run: sh gradlew clean build working-directory: ./SportsApp @@ -15,6 +17,9 @@ jobs: deploy: name: Sample App Deploy runs-on: self-hosted + defaults: + run: + working-directory: /opt/sampleapp steps: - name: Download the application artifact run: pwd && wget https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz --no-check-certificate From 6076126a07f1d1daca73c802f894e2ce36f2c174 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Thu, 7 Sep 2023 10:41:29 -0400 Subject: [PATCH 025/135] more debug --- .github/workflows/github-actions-demo.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 55d8cdb..82d4f0b 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -21,12 +21,15 @@ jobs: run: working-directory: /opt/sampleapp steps: + - name: Clean-up + run: rm -Rf * - name: Download the application artifact run: pwd && wget https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz --no-check-certificate - name: Extract the artifact run: tar -zxf sportsapp-1.0.0.tar.gz + - run: mkdir ./deploy/license - name: Download the OpenEdge License file - run: wget -cO - https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > progress.cfg --no-check-certificate + run: wget -cO - https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate From fa534e7c903a1857a6ebae885e13f208a0d8708e Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Thu, 7 Sep 2023 10:47:14 -0400 Subject: [PATCH 026/135] added undeploy and deploy to deploy steps --- .github/workflows/github-actions-demo.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 82d4f0b..01ef78b 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -30,6 +30,14 @@ jobs: - run: mkdir ./deploy/license - name: Download the OpenEdge License file run: wget -cO - https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate + - name: Undeploy previous version of Sample App + run: sudo sh undeploy.sh + working-directory: ./deploy + - name: Deploy new version of Sample App + run: sudo sh deploy.sh + working-directory: ./deploy + + From ce3e2cd7a4d07f7ff79e3ff2801f5406072165c4 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Thu, 7 Sep 2023 10:50:48 -0400 Subject: [PATCH 027/135] added correct working-dir --- .github/workflows/github-actions-demo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 01ef78b..9a826f0 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -32,10 +32,10 @@ jobs: run: wget -cO - https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate - name: Undeploy previous version of Sample App run: sudo sh undeploy.sh - working-directory: ./deploy + working-directory: /opt/sampleapp/deploy - name: Deploy new version of Sample App run: sudo sh deploy.sh - working-directory: ./deploy + working-directory: /opt/sampleapp/deploy From 235552e209e370506491bcb8df9f496ddf869a30 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Thu, 7 Sep 2023 11:31:42 -0400 Subject: [PATCH 028/135] update of local images --- SportsApp/deploy/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SportsApp/deploy/docker-compose.yml b/SportsApp/deploy/docker-compose.yml index 8f497ed..4e0375f 100644 --- a/SportsApp/deploy/docker-compose.yml +++ b/SportsApp/deploy/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.6" services: oedbmachine: - image: oedb:latest + image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-db:12.8.0 ports: - "7654:7654" - "7664-7684:7664-7684" @@ -24,7 +24,7 @@ services: volumes: - app_dc:/deploy-staging/artifacts pasoeinstance: - image: progresssoftware/prgs-pasoe:12.8.0 + image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0 depends_on: - jdk - ablapp From 60aaddf827e34050392db1fb1a250791b6e04b9f Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 7 Sep 2023 21:24:24 +0530 Subject: [PATCH 029/135] fix sports app url --- SportsApp/webui/grid.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SportsApp/webui/grid.js b/SportsApp/webui/grid.js index 04b5078..0b5b348 100644 --- a/SportsApp/webui/grid.js +++ b/SportsApp/webui/grid.js @@ -4,7 +4,7 @@ $(function () { 'use strict'; - var serviceURI = "https://:8811/Sports"; + var serviceURI = "https://localhost:8811/Sports"; var catalogURI = serviceURI + "/static/SportsService.json"; function createGrid() { From 85bcb37c87ade9c0324702917ec48dc217e1e9a9 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 7 Sep 2023 22:29:06 +0530 Subject: [PATCH 030/135] fix sports app url --- SportsApp/webui/grid.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SportsApp/webui/grid.js b/SportsApp/webui/grid.js index 0b5b348..e6c9df0 100644 --- a/SportsApp/webui/grid.js +++ b/SportsApp/webui/grid.js @@ -4,7 +4,7 @@ $(function () { 'use strict'; - var serviceURI = "https://localhost:8811/Sports"; + var serviceURI = "https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports"; var catalogURI = serviceURI + "/static/SportsService.json"; function createGrid() { From 643005c31c01509fd83cba78440e07dfef4ef3f1 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Fri, 8 Sep 2023 14:10:25 -0400 Subject: [PATCH 031/135] updates to test --- .github/workflows/github-actions-demo.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml index 9a826f0..dcdda9c 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/github-actions-demo.yml @@ -8,7 +8,7 @@ jobs: steps: - name: Clean-up run: rm -Rf * - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: sh gradlew clean build working-directory: ./SportsApp - name: upload zip file to nexus @@ -16,6 +16,7 @@ jobs: working-directory: ./SportsApp deploy: name: Sample App Deploy + needs: compile runs-on: self-hosted defaults: run: @@ -36,6 +37,17 @@ jobs: - name: Deploy new version of Sample App run: sudo sh deploy.sh working-directory: /opt/sampleapp/deploy + test: + name: Test deploy of Sample App + needs: deploy + runs-on: self-hosted + defaults: + run: + working-directory: /opt/sampleapp + steps: + - name: Check application life + run: wget -cO - https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports/rest/SportsService/Customer > customer.json --no-check-certificate + From e361e6277919f9cd6761856e2ad67bc537050f32 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Fri, 8 Sep 2023 15:24:56 -0400 Subject: [PATCH 032/135] adding second workflow --- .../{github-actions-demo.yml => development.yml} | 2 +- .github/workflows/release.yml | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) rename .github/workflows/{github-actions-demo.yml => development.yml} (99%) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/development.yml similarity index 99% rename from .github/workflows/github-actions-demo.yml rename to .github/workflows/development.yml index dcdda9c..190f3ed 100644 --- a/.github/workflows/github-actions-demo.yml +++ b/.github/workflows/development.yml @@ -1,4 +1,4 @@ -name: OpenEdgeOps +name: Development run-name: ${{ github.actor }} is compiling our Sample App 🚀 on: [push] jobs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a1cb592 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,11 @@ +name: Release +run-name: ${{ github.actor }} is releaseing our Sample App 🚀 +on: + release: + types: [published] +jobs: + release: + name: Sample App release + runs-on: self-hosted + steps: + run: pwd From 7e1e6835bfd51aefd31abb4991834db4e684a984 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Fri, 8 Sep 2023 15:32:15 -0400 Subject: [PATCH 033/135] corrected syntax --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a1cb592..303da3a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,4 +8,4 @@ jobs: name: Sample App release runs-on: self-hosted steps: - run: pwd + - run: pwd From a982d44052011d1a03cb1a508051cffe142c9dea Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Fri, 8 Sep 2023 15:34:18 -0400 Subject: [PATCH 034/135] Comment out test --- .github/workflows/development.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 190f3ed..8cd72a2 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -37,16 +37,16 @@ jobs: - name: Deploy new version of Sample App run: sudo sh deploy.sh working-directory: /opt/sampleapp/deploy - test: - name: Test deploy of Sample App - needs: deploy - runs-on: self-hosted - defaults: - run: - working-directory: /opt/sampleapp - steps: - - name: Check application life - run: wget -cO - https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports/rest/SportsService/Customer > customer.json --no-check-certificate +# test: +# name: Test deploy of Sample App +# needs: deploy +# runs-on: self-hosted +# defaults: +# run: +# working-directory: /opt/sampleapp +# steps: +# - name: Check application life +# run: wget -cO - https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports/rest/SportsService/Customer > customer.json --no-check-certificate From 564d7a5c8e6844ac2897c29153fc7a7b6ed2eaf0 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Fri, 8 Sep 2023 15:58:07 -0400 Subject: [PATCH 035/135] fine tune build --- .github/workflows/development.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 8cd72a2..3c5b6d5 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -12,7 +12,7 @@ jobs: - run: sh gradlew clean build working-directory: ./SportsApp - name: upload zip file to nexus - run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz + run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz working-directory: ./SportsApp deploy: name: Sample App Deploy @@ -20,23 +20,23 @@ jobs: runs-on: self-hosted defaults: run: - working-directory: /opt/sampleapp + working-directory: ${{vars.SAMPLEAPP_DIR}} steps: - name: Clean-up run: rm -Rf * - name: Download the application artifact - run: pwd && wget https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz --no-check-certificate + run: wget ${{vars.NEXUS_URL}}/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz --no-check-certificate - name: Extract the artifact run: tar -zxf sportsapp-1.0.0.tar.gz - run: mkdir ./deploy/license - name: Download the OpenEdge License file - run: wget -cO - https://ec2-54-80-142-101.compute-1.amazonaws.com:8443/repository/PugChallengeMaven/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate + run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate - name: Undeploy previous version of Sample App run: sudo sh undeploy.sh - working-directory: /opt/sampleapp/deploy + working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy - name: Deploy new version of Sample App run: sudo sh deploy.sh - working-directory: /opt/sampleapp/deploy + working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy # test: # name: Test deploy of Sample App # needs: deploy From 3cc42570c02688d37f0303b1f2395206d0bd9952 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Fri, 8 Sep 2023 16:04:01 -0400 Subject: [PATCH 036/135] added vars --- .github/workflows/development.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 3c5b6d5..1821950 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -12,7 +12,7 @@ jobs: - run: sh gradlew clean build working-directory: ./SportsApp - name: upload zip file to nexus - run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz + run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/${{vars.SAMPLEAPP_NAME}}.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{var.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz working-directory: ./SportsApp deploy: name: Sample App Deploy @@ -25,9 +25,9 @@ jobs: - name: Clean-up run: rm -Rf * - name: Download the application artifact - run: wget ${{vars.NEXUS_URL}}/com/progess/sportsapp/1.0.0/sportsapp-1.0.0.tar.gz --no-check-certificate + run: wget ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz --no-check-certificate - name: Extract the artifact - run: tar -zxf sportsapp-1.0.0.tar.gz + run: tar -zxf ${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz - run: mkdir ./deploy/license - name: Download the OpenEdge License file run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate From cc2965170dbba47578b7e82699938c94bba604e2 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Fri, 8 Sep 2023 16:04:44 -0400 Subject: [PATCH 037/135] update --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 1821950..657db51 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -12,7 +12,7 @@ jobs: - run: sh gradlew clean build working-directory: ./SportsApp - name: upload zip file to nexus - run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/${{vars.SAMPLEAPP_NAME}}.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{var.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz + run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/${{vars.SAMPLEAPP_NAME}}.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz working-directory: ./SportsApp deploy: name: Sample App Deploy From c35f6909d2aefa086485a7bc77d69c1eff2bc87d Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Fri, 8 Sep 2023 16:07:27 -0400 Subject: [PATCH 038/135] fixed case --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 657db51..fb06e48 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -12,7 +12,7 @@ jobs: - run: sh gradlew clean build working-directory: ./SportsApp - name: upload zip file to nexus - run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/${{vars.SAMPLEAPP_NAME}}.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz + run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz working-directory: ./SportsApp deploy: name: Sample App Deploy From e70ac11b61cb76ffa71a8bb3c9e9172d4b7109f2 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 20:20:33 +0530 Subject: [PATCH 039/135] ABL unit test --- SportsApp/Sports/.dbconnection | 4 + SportsApp/Sports/.gitignore | 1 + SportsApp/Sports/.propath | 20 +-- SportsApp/Sports/.settings/pasoe.config.xml | 7 +- SportsApp/Sports/build.gradle | 26 +++- SportsApp/Sports/tests/CustomerTest.cls | 162 ++++++++++++++++++++ SportsApp/Sports/tests/OrderTest.cls | 43 ++++++ SportsApp/Sports/tests/SportsTestSuite.cls | 16 ++ SportsApp/deploy/.gitignore | 2 + SportsApp/deploy/ablapps/.gitignore | 4 + SportsApp/deploy/license/.gitignore | 4 + 11 files changed, 274 insertions(+), 15 deletions(-) create mode 100644 SportsApp/Sports/.dbconnection create mode 100644 SportsApp/Sports/tests/CustomerTest.cls create mode 100644 SportsApp/Sports/tests/OrderTest.cls create mode 100644 SportsApp/Sports/tests/SportsTestSuite.cls create mode 100644 SportsApp/deploy/.gitignore create mode 100644 SportsApp/deploy/ablapps/.gitignore create mode 100644 SportsApp/deploy/license/.gitignore diff --git a/SportsApp/Sports/.dbconnection b/SportsApp/Sports/.dbconnection new file mode 100644 index 0000000..0b808fd --- /dev/null +++ b/SportsApp/Sports/.dbconnection @@ -0,0 +1,4 @@ + + + + diff --git a/SportsApp/Sports/.gitignore b/SportsApp/Sports/.gitignore index 1b6985c..5a57b4e 100644 --- a/SportsApp/Sports/.gitignore +++ b/SportsApp/Sports/.gitignore @@ -3,3 +3,4 @@ # Ignore Gradle build output directory build +build-output diff --git a/SportsApp/Sports/.propath b/SportsApp/Sports/.propath index 4be5f15..5ed6576 100644 --- a/SportsApp/Sports/.propath +++ b/SportsApp/Sports/.propath @@ -1,11 +1,13 @@ - - - - - - - - - + + + + + + + + + + + diff --git a/SportsApp/Sports/.settings/pasoe.config.xml b/SportsApp/Sports/.settings/pasoe.config.xml index ab5710d..91613ca 100644 --- a/SportsApp/Sports/.settings/pasoe.config.xml +++ b/SportsApp/Sports/.settings/pasoe.config.xml @@ -1,7 +1,4 @@ - -Sports - -oepas1 in nbhydrahulk10.oepas1 (Progress Application Server for OpenEdge 12.0ALPHA) - + + Sports diff --git a/SportsApp/Sports/build.gradle b/SportsApp/Sports/build.gradle index 649ccd9..1eb78b9 100644 --- a/SportsApp/Sports/build.gradle +++ b/SportsApp/Sports/build.gradle @@ -88,6 +88,28 @@ if (STAGE_ENVIRONMENT != "prod" || STAGE_ENVIRONMENT == "staging") { compileABLApp.dependsOn "createSports2020" } +task testABLApp(type: ABLUnit){ + source("tests") + include("**/*Suite.cls") + propath("tests", "AppServer") + outputDir = "${buildDir}/test-results/test" + arguments = [haltOnFailure: "true"] + + if (STAGE_ENVIRONMENT == "prod" || STAGE_ENVIRONMENT == "staging") { + dbConnection { + parameterFile='conf/startup.pf' + } + } else { + dbConnection { + dbName="${buildDir}/db/sports2020/sports" + connectionParameters = "-1" + } + } +} +if (STAGE_ENVIRONMENT != "prod" || STAGE_ENVIRONMENT == "staging") { + testABLApp.dependsOn "createSports2020" +} + task packageABLApp(type: Oear){ ablAppName = "Sports" destinationDirectory = project.distsDirectory //will create 'Sports.oear' file at this location @@ -117,4 +139,6 @@ task packageABLApp(type: Oear){ } packageABLApp.dependsOn "compileABLApp" packageABLApp.dependsOn "packageWebApp" -build.dependsOn "packageABLApp" \ No newline at end of file + +assemble.dependsOn "packageABLApp" +build.dependsOn "testABLApp" \ No newline at end of file diff --git a/SportsApp/Sports/tests/CustomerTest.cls b/SportsApp/Sports/tests/CustomerTest.cls new file mode 100644 index 0000000..3f4a8b6 --- /dev/null +++ b/SportsApp/Sports/tests/CustomerTest.cls @@ -0,0 +1,162 @@ + +/*------------------------------------------------------------------------ + File : CustomerTest + Purpose : + Syntax : + Description : + Author(s) : rahulk + Created : Sat Sep 09 16:43:47 IST 2023 + Notes : + ----------------------------------------------------------------------*/ + +USING Progress.Lang.*. +USING OpenEdge.Core.Assert FROM PROPATH. + +BLOCK-LEVEL ON ERROR UNDO, THROW. + +CLASS CustomerTest: + + /* DEFINE TEMP-TABLE ttOrder */ + /* FIELD Ordernum AS INTEGER LABEL "Order Num" FORMAT "zzzzzzzzz9" INITIAL "0".*/ + /* DEFINE DATASET dsCustomer1 FOR ttOrder. */ + /* + {} */ + +/* {customer.i}*/ + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @Before. + METHOD PUBLIC VOID setUpBeforeClass( ): + + RETURN. + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @Setup. + METHOD PUBLIC VOID setUp( ): + + RETURN. + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @TearDown. + METHOD PUBLIC VOID tearDown( ): + + RETURN. + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @After. + METHOD PUBLIC VOID tearDownAfterClass( ): + + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testReadCustomer( ): +/* DEFINE VARIABLE customerVar AS Customer NO-UNDO. */ +/* DEFINE VARIABLE iQuery AS CHARACTER NO-UNDO. */ +/* */ +/* customerVar = NEW Customer(). */ +/* iQuery="WHERE Customer.State = 'UNKNOWN'". */ +/* */ +/* DEFINE DATA-SOURCE srcCustomer FOR Customer. */ +/* /* DEFINE BUFFER OtherCust FOR Customer. */ */ +/* /* */ */ +/* /* DATASET dsCustomer:FILL(). */ */ +/* /* */ */ +/* /* DATASET dsCustomer:HANDLE. */ */ +/* */ +/* BUFFER ttCustomer:ATTACH-DATA-SOURCE(DATA-SOURCE srcCustomer:HANDLE,*/ +/* "Customer.Name,CustName"). */ +/* */ +/* customerVar:ReadCustomer(iQuery, DATASET dsCustomer). */ +/* */ +/* DATASET dsCustomer:FILL(). */ +/* BUFFER ttCustomer:DETACH-DATA-SOURCE(). */ +/* FOR EACH ttCustomer: */ +/* DISPLAY */ +/* ttCustomer.Name */ +/* ttCustomer.State. */ +/* Assert:Equals(ttCustomer.State, "hey"). */ +/* */ +/* END. */ + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testCreateCustomer( ): + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testcount( ): + DEFINE VARIABLE customerVar AS Customer NO-UNDO. + DEFINE VARIABLE iQuery AS CHARACTER NO-UNDO. + DEFINE VARIABLE oCount AS INTEGER NO-UNDO. + + customerVar = NEW Customer(). + iQuery="WHERE Customer.State = 'UNKNOWN'". + customerVar:count(iQuery, oCount). + Assert:Equals(oCount , 0). + + iQuery="WHERE Customer.State = 'NH'". + customerVar:count(iQuery, oCount). + Assert:NotEqual(oCount , 0). + Assert:IsPositive(oCount). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testDeleteCustomer( ): + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + +END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/tests/OrderTest.cls b/SportsApp/Sports/tests/OrderTest.cls new file mode 100644 index 0000000..11c63ab --- /dev/null +++ b/SportsApp/Sports/tests/OrderTest.cls @@ -0,0 +1,43 @@ + +/*------------------------------------------------------------------------ + File : OrderTest + Purpose : + Syntax : + Description : + Author(s) : rahulk + Created : Sat Sep 09 19:40:47 IST 2023 + Notes : + ----------------------------------------------------------------------*/ + +USING Progress.Lang.*. +USING OpenEdge.Core.Assert FROM PROPATH. + +BLOCK-LEVEL ON ERROR UNDO, THROW. + +CLASS OrderTest: + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testCreateOrder( ): + Assert:IsTrue(FALSE). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testReadOrder( ): + Assert:IsTrue(FALSE). + RETURN. + + END METHOD. + +END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/tests/SportsTestSuite.cls b/SportsApp/Sports/tests/SportsTestSuite.cls new file mode 100644 index 0000000..516839c --- /dev/null +++ b/SportsApp/Sports/tests/SportsTestSuite.cls @@ -0,0 +1,16 @@ + + /*------------------------------------------------------------------------ + File : SportsTestSuite + Syntax : + Author(s) : rahulk + Created : Sat Sep 09 16:41:21 IST 2023 + Notes : + ----------------------------------------------------------------------*/ + +USING Progress.Lang.*. +BLOCK-LEVEL ON ERROR UNDO, THROW. + +@TestSuite(classes= "CustomerTest, OrderTest"). +CLASS SportsTestSuite: + +END CLASS. \ No newline at end of file diff --git a/SportsApp/deploy/.gitignore b/SportsApp/deploy/.gitignore new file mode 100644 index 0000000..5629c95 --- /dev/null +++ b/SportsApp/deploy/.gitignore @@ -0,0 +1,2 @@ +output +webui \ No newline at end of file diff --git a/SportsApp/deploy/ablapps/.gitignore b/SportsApp/deploy/ablapps/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/SportsApp/deploy/ablapps/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/SportsApp/deploy/license/.gitignore b/SportsApp/deploy/license/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/SportsApp/deploy/license/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file From 3ae4242fc308665c3efdab0fcf049196836f1e37 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 20:27:42 +0530 Subject: [PATCH 040/135] Report ABL unit test results --- .github/workflows/development.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index fb06e48..4acce59 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -10,7 +10,15 @@ jobs: run: rm -Rf * - uses: actions/checkout@v3 - run: sh gradlew clean build - working-directory: ./SportsApp + working-directory: ./SportsApp + - name: Test report + uses: dorny/test-reporter@v1 + if: success() || failure() + with: + name: ABL Unit Tests # Name of the check run which will be created + path: test-results/test/*.xml # Path to test results + reporter: jest-junit # Format of test results + working-directory: ./SportsApp/Sports/build - name: upload zip file to nexus run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz working-directory: ./SportsApp From bd3c4d1efa9cf3f644d0449f886d248cc2c93752 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 20:29:56 +0530 Subject: [PATCH 041/135] Report ABL unit test results --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 4acce59..2b1234a 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -18,7 +18,7 @@ jobs: name: ABL Unit Tests # Name of the check run which will be created path: test-results/test/*.xml # Path to test results reporter: jest-junit # Format of test results - working-directory: ./SportsApp/Sports/build + working-directory: ./SportsApp/Sports/build - name: upload zip file to nexus run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz working-directory: ./SportsApp From 820ff6b0e7cc9ee4af76e2c063f4689f0b0506d9 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 20:38:57 +0530 Subject: [PATCH 042/135] debug unit test report --- .github/workflows/development.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 2b1234a..2a5735a 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -16,9 +16,9 @@ jobs: if: success() || failure() with: name: ABL Unit Tests # Name of the check run which will be created - path: test-results/test/*.xml # Path to test results + path: results.xml # Path to test results reporter: jest-junit # Format of test results - working-directory: ./SportsApp/Sports/build + working-directory: ./SportsApp/Sports/build/test-results/test - name: upload zip file to nexus run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz working-directory: ./SportsApp From 2db2b010172800741d1dd22714c206fbf059554b Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 20:49:41 +0530 Subject: [PATCH 043/135] undo false test failures --- SportsApp/Sports/tests/OrderTest.cls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SportsApp/Sports/tests/OrderTest.cls b/SportsApp/Sports/tests/OrderTest.cls index 11c63ab..ff81736 100644 --- a/SportsApp/Sports/tests/OrderTest.cls +++ b/SportsApp/Sports/tests/OrderTest.cls @@ -23,7 +23,7 @@ CLASS OrderTest: ------------------------------------------------------------------------------*/ @Test. METHOD PUBLIC VOID testCreateOrder( ): - Assert:IsTrue(FALSE). + Assert:IsTrue(TRUE). RETURN. END METHOD. @@ -35,7 +35,7 @@ CLASS OrderTest: ------------------------------------------------------------------------------*/ @Test. METHOD PUBLIC VOID testReadOrder( ): - Assert:IsTrue(FALSE). + Assert:IsTrue(TRUE). RETURN. END METHOD. From 8566cb5789fe94c599abb5957f21b21813f468d9 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 20:57:15 +0530 Subject: [PATCH 044/135] try setting permissions --- .github/workflows/development.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 2a5735a..a9d6d6b 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -3,6 +3,7 @@ run-name: ${{ github.actor }} is compiling our Sample App 🚀 on: [push] jobs: compile: + permissions: write-all name: OpenEdge Compile job runs-on: self-hosted steps: From e743151c996cf19a69a44248e1006993cfd5a98b Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 20:59:35 +0530 Subject: [PATCH 045/135] use java junit format --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index a9d6d6b..6aa3bcb 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -18,7 +18,7 @@ jobs: with: name: ABL Unit Tests # Name of the check run which will be created path: results.xml # Path to test results - reporter: jest-junit # Format of test results + reporter: java-junit # Format of test results working-directory: ./SportsApp/Sports/build/test-results/test - name: upload zip file to nexus run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz From 97d872614db62e07f2dff079cfda87aa3fc0b003 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 21:09:21 +0530 Subject: [PATCH 046/135] upload test results --- .github/workflows/development.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 6aa3bcb..605346d 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -12,14 +12,18 @@ jobs: - uses: actions/checkout@v3 - run: sh gradlew clean build working-directory: ./SportsApp + - uses: actions/upload-artifact@v2 # upload test results + if: success() || failure() # run this step even if previous step failed + with: + name: test-results + path: ./SportsApp/Sports/build/test-results/test/results.xml - name: Test report uses: dorny/test-reporter@v1 if: success() || failure() with: name: ABL Unit Tests # Name of the check run which will be created - path: results.xml # Path to test results + path: ./SportsApp/Sports/build/test-results/test/results.xml # Path to test results reporter: java-junit # Format of test results - working-directory: ./SportsApp/Sports/build/test-results/test - name: upload zip file to nexus run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz working-directory: ./SportsApp From 51878a8444f0101831d064cf45aa0d99d2ebaec6 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 21:13:15 +0530 Subject: [PATCH 047/135] upload test results --- .github/workflows/development.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 605346d..f006b41 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -22,7 +22,8 @@ jobs: if: success() || failure() with: name: ABL Unit Tests # Name of the check run which will be created - path: ./SportsApp/Sports/build/test-results/test/results.xml # Path to test results + artifact: test-results + path: '*.xml' # Path to test results reporter: java-junit # Format of test results - name: upload zip file to nexus run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz From ce8e1763272df6bb2b76783480d1b94102b72712 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 21:19:56 +0530 Subject: [PATCH 048/135] test report workflow --- .github/workflows/development.yml | 9 --------- .github/workflows/test-report.yml | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/test-report.yml diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index f006b41..ec7702e 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -3,7 +3,6 @@ run-name: ${{ github.actor }} is compiling our Sample App 🚀 on: [push] jobs: compile: - permissions: write-all name: OpenEdge Compile job runs-on: self-hosted steps: @@ -17,14 +16,6 @@ jobs: with: name: test-results path: ./SportsApp/Sports/build/test-results/test/results.xml - - name: Test report - uses: dorny/test-reporter@v1 - if: success() || failure() - with: - name: ABL Unit Tests # Name of the check run which will be created - artifact: test-results - path: '*.xml' # Path to test results - reporter: java-junit # Format of test results - name: upload zip file to nexus run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz working-directory: ./SportsApp diff --git a/.github/workflows/test-report.yml b/.github/workflows/test-report.yml new file mode 100644 index 0000000..557ef36 --- /dev/null +++ b/.github/workflows/test-report.yml @@ -0,0 +1,17 @@ +name: 'Test Report' +on: + workflow_run: + workflows: ['Development'] # runs after CI workflow + types: + - completed +jobs: + report: + permissions: write-all + runs-on: self-hosted + steps: + - uses: dorny/test-reporter@v1 + with: + name: ABL Unit Tests # Name of the check run which will be created + artifact: test-results + path: '*.xml' # Path to test results + reporter: java-junit # Format of test results From f9203d65b0be58a2c6b5dd7b5d5c431cfa6f0f6c Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 21:25:38 +0530 Subject: [PATCH 049/135] debug reporting --- .github/workflows/development.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index ec7702e..98e38ca 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -42,6 +42,17 @@ jobs: - name: Deploy new version of Sample App run: sudo sh deploy.sh working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy + report: + permissions: write-all + needs: compile + runs-on: self-hosted + steps: + - uses: dorny/test-reporter@v1 + with: + name: ABL Unit Tests # Name of the check run which will be created + artifact: test-results + path: '*.xml' # Path to test results + reporter: java-junit # Format of test results # test: # name: Test deploy of Sample App # needs: deploy From 4586a85630ddc8473f4e99fcff9cff247f7e8eff Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 21:41:12 +0530 Subject: [PATCH 050/135] reporting unit test --- .github/workflows/development.yml | 12 ++++++++---- .github/workflows/test-report.yml | 17 ----------------- 2 files changed, 8 insertions(+), 21 deletions(-) delete mode 100644 .github/workflows/test-report.yml diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 98e38ca..329a9c0 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -47,12 +47,16 @@ jobs: needs: compile runs-on: self-hosted steps: + - name: Retrieve saved test report + uses: actions/download-artifact@v2 + with: + name: test-results + path: test-results/test/results.xml - uses: dorny/test-reporter@v1 with: - name: ABL Unit Tests # Name of the check run which will be created - artifact: test-results - path: '*.xml' # Path to test results - reporter: java-junit # Format of test results + name: ABL Unit Tests # Name of the check run which will be created + path: test-results/test/results.xml # Path to test results + reporter: java-junit # Format of test results # test: # name: Test deploy of Sample App # needs: deploy diff --git a/.github/workflows/test-report.yml b/.github/workflows/test-report.yml deleted file mode 100644 index 557ef36..0000000 --- a/.github/workflows/test-report.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: 'Test Report' -on: - workflow_run: - workflows: ['Development'] # runs after CI workflow - types: - - completed -jobs: - report: - permissions: write-all - runs-on: self-hosted - steps: - - uses: dorny/test-reporter@v1 - with: - name: ABL Unit Tests # Name of the check run which will be created - artifact: test-results - path: '*.xml' # Path to test results - reporter: java-junit # Format of test results From 29954168e9445e52d49a021d1f34edb6430542ed Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 21:46:23 +0530 Subject: [PATCH 051/135] reporting unit test --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 329a9c0..faa7a13 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -51,7 +51,7 @@ jobs: uses: actions/download-artifact@v2 with: name: test-results - path: test-results/test/results.xml + path: test-results/test - uses: dorny/test-reporter@v1 with: name: ABL Unit Tests # Name of the check run which will be created From e98877922a499553ceff61037d53bcd26f3adb8b Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 22:03:42 +0530 Subject: [PATCH 052/135] use latest version of test reporter --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index faa7a13..bd5e20f 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -52,7 +52,7 @@ jobs: with: name: test-results path: test-results/test - - uses: dorny/test-reporter@v1 + - uses: dorny/test-reporter@v1.6.0 with: name: ABL Unit Tests # Name of the check run which will be created path: test-results/test/results.xml # Path to test results From a48c6d3c51b52346efdf9d2bffbc4e1ad18d0c04 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 22:10:20 +0530 Subject: [PATCH 053/135] use latest version of test reporter --- .github/workflows/development.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index bd5e20f..db9eb73 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -43,6 +43,7 @@ jobs: run: sudo sh deploy.sh working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy report: + name: Reports permissions: write-all needs: compile runs-on: self-hosted @@ -54,9 +55,15 @@ jobs: path: test-results/test - uses: dorny/test-reporter@v1.6.0 with: - name: ABL Unit Tests # Name of the check run which will be created + name: ABL Unit Test Results # Name of the check run which will be created path: test-results/test/results.xml # Path to test results reporter: java-junit # Format of test results + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: | + test-results/**/*.xml # test: # name: Test deploy of Sample App # needs: deploy From 03ecd6960b5ec7e5c8ad97f2f9c93bd0446f92aa Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 22:19:32 +0530 Subject: [PATCH 054/135] try different plugin to publish report --- .github/workflows/development.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index db9eb73..835bd5d 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -58,12 +58,14 @@ jobs: name: ABL Unit Test Results # Name of the check run which will be created path: test-results/test/results.xml # Path to test results reporter: java-junit # Format of test results + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.8 - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() + uses: EnricoMi/publish-unit-test-result-action/composite@v2 with: - files: | - test-results/**/*.xml + files: test-results/**/*.xml # test: # name: Test deploy of Sample App # needs: deploy From 3023636ef7514c3e6729bb4e32c2a6da990ac490 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 22:26:15 +0530 Subject: [PATCH 055/135] fail tests deliberately --- SportsApp/Sports/tests/OrderTest.cls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SportsApp/Sports/tests/OrderTest.cls b/SportsApp/Sports/tests/OrderTest.cls index ff81736..11c63ab 100644 --- a/SportsApp/Sports/tests/OrderTest.cls +++ b/SportsApp/Sports/tests/OrderTest.cls @@ -23,7 +23,7 @@ CLASS OrderTest: ------------------------------------------------------------------------------*/ @Test. METHOD PUBLIC VOID testCreateOrder( ): - Assert:IsTrue(TRUE). + Assert:IsTrue(FALSE). RETURN. END METHOD. @@ -35,7 +35,7 @@ CLASS OrderTest: ------------------------------------------------------------------------------*/ @Test. METHOD PUBLIC VOID testReadOrder( ): - Assert:IsTrue(TRUE). + Assert:IsTrue(FALSE). RETURN. END METHOD. From 02672b82a6aa0eb5ed8d6186f08a734fbcdad090 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 22:30:54 +0530 Subject: [PATCH 056/135] run report job even after failures --- .github/workflows/development.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 835bd5d..b07b5f7 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -46,6 +46,7 @@ jobs: name: Reports permissions: write-all needs: compile + if: always() runs-on: self-hosted steps: - name: Retrieve saved test report From 3782839f62264fd3e7c6cde002beeaaf7c5eb68a Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sat, 9 Sep 2023 22:38:15 +0530 Subject: [PATCH 057/135] undo test failures --- .github/workflows/development.yml | 12 ++++++------ SportsApp/Sports/tests/OrderTest.cls | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index b07b5f7..7fff69f 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -43,7 +43,7 @@ jobs: run: sudo sh deploy.sh working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy report: - name: Reports + name: Publish Reports permissions: write-all needs: compile if: always() @@ -54,11 +54,11 @@ jobs: with: name: test-results path: test-results/test - - uses: dorny/test-reporter@v1.6.0 - with: - name: ABL Unit Test Results # Name of the check run which will be created - path: test-results/test/results.xml # Path to test results - reporter: java-junit # Format of test results + # - uses: dorny/test-reporter@v1.6.0 + # with: + # name: ABL Unit Test Results # Name of the check run which will be created + # path: test-results/test/results.xml # Path to test results + # reporter: java-junit # Format of test results - name: Setup Python uses: actions/setup-python@v4 with: diff --git a/SportsApp/Sports/tests/OrderTest.cls b/SportsApp/Sports/tests/OrderTest.cls index 11c63ab..ff81736 100644 --- a/SportsApp/Sports/tests/OrderTest.cls +++ b/SportsApp/Sports/tests/OrderTest.cls @@ -23,7 +23,7 @@ CLASS OrderTest: ------------------------------------------------------------------------------*/ @Test. METHOD PUBLIC VOID testCreateOrder( ): - Assert:IsTrue(FALSE). + Assert:IsTrue(TRUE). RETURN. END METHOD. @@ -35,7 +35,7 @@ CLASS OrderTest: ------------------------------------------------------------------------------*/ @Test. METHOD PUBLIC VOID testReadOrder( ): - Assert:IsTrue(FALSE). + Assert:IsTrue(TRUE). RETURN. END METHOD. From d4daac0b5c54a40481a4d9db30fd9be41e775fc0 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 10 Sep 2023 02:26:06 +0530 Subject: [PATCH 058/135] docker image scan --- .github/workflows/development.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 7fff69f..b6f0649 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -42,6 +42,16 @@ jobs: - name: Deploy new version of Sample App run: sudo sh deploy.sh working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: 'docker.io/sports:latest' + format: 'sarif' + output: 'trivy-results.sarif' + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: 'trivy-results.sarif' report: name: Publish Reports permissions: write-all From 3bf19325d8f8f7d2dc87a640c388f328a3a26ddb Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 10 Sep 2023 02:46:21 +0530 Subject: [PATCH 059/135] trigger build --- .github/workflows/development.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index b6f0649..5557292 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -88,10 +88,6 @@ jobs: # - name: Check application life # run: wget -cO - https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports/rest/SportsService/Customer > customer.json --no-check-certificate - - - - From cceb2af34bfea7fd87b5a2deaea36b0caa5bd481 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 10 Sep 2023 03:08:02 +0530 Subject: [PATCH 060/135] permission for docker scan --- .github/workflows/development.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 5557292..3bc2319 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -22,6 +22,7 @@ jobs: deploy: name: Sample App Deploy needs: compile + permissions: write-all runs-on: self-hosted defaults: run: From ffdb0a02b85907aaf1573548d28a36a7d8dacc05 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 10 Sep 2023 04:01:00 +0530 Subject: [PATCH 061/135] trivy scan for paose image --- .github/workflows/development.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 3bc2319..c24c7ba 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -43,16 +43,26 @@ jobs: - name: Deploy new version of Sample App run: sudo sh deploy.sh working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy - - name: Run Trivy vulnerability scanner + - name: Run Trivy vulnerability scanner for 'sports:latest' uses: aquasecurity/trivy-action@master with: image-ref: 'docker.io/sports:latest' format: 'sarif' output: 'trivy-results.sarif' - - name: Upload Trivy scan results to GitHub Security tab + - name: Upload Trivy scan results to GitHub Security tab for 'sports:latest' uses: github/codeql-action/upload-sarif@v2 with: sarif_file: 'trivy-results.sarif' + - name: Run Trivy vulnerability scanner for 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0' + uses: aquasecurity/trivy-action@master + with: + image-ref: 'docker.io/ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0' + format: 'sarif' + output: 'trivy-results-pasoe.sarif' + - name: Upload Trivy scan results to GitHub Security tab for 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0' + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: 'trivy-results-pasoe.sarif' report: name: Publish Reports permissions: write-all From c100dcf939480eca282d6dd9f32c42f16f3b96de Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 10 Sep 2023 04:05:17 +0530 Subject: [PATCH 062/135] fix pasoe image name --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index c24c7ba..79eef76 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -56,7 +56,7 @@ jobs: - name: Run Trivy vulnerability scanner for 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0' uses: aquasecurity/trivy-action@master with: - image-ref: 'docker.io/ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0' + image-ref: 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0' format: 'sarif' output: 'trivy-results-pasoe.sarif' - name: Upload Trivy scan results to GitHub Security tab for 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0' From b3a5b89e18067f77fc49e9459f0617afc2ad1c6e Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 10 Sep 2023 04:11:31 +0530 Subject: [PATCH 063/135] test JDK security scan --- .github/workflows/development.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 79eef76..434364e 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -53,16 +53,16 @@ jobs: uses: github/codeql-action/upload-sarif@v2 with: sarif_file: 'trivy-results.sarif' - - name: Run Trivy vulnerability scanner for 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0' + - name: Run Trivy vulnerability scanner for 'eclipse-temurin:17.0.3_7-jdk-centos7' uses: aquasecurity/trivy-action@master with: - image-ref: 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0' + image-ref: 'eclipse-temurin:17.0.3_7-jdk-centos7 ' format: 'sarif' - output: 'trivy-results-pasoe.sarif' - - name: Upload Trivy scan results to GitHub Security tab for 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0' + output: 'trivy-results-jdk.sarif' + - name: Upload Trivy scan results to GitHub Security tab for 'eclipse-temurin:17.0.3_7-jdk-centos7' uses: github/codeql-action/upload-sarif@v2 with: - sarif_file: 'trivy-results-pasoe.sarif' + sarif_file: 'trivy-results-jdk.sarif' report: name: Publish Reports permissions: write-all From 9fc8f0f9cf7dd5599634f766d65e9176c138c15f Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 10 Sep 2023 04:12:18 +0530 Subject: [PATCH 064/135] space --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 434364e..640f7df 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -56,7 +56,7 @@ jobs: - name: Run Trivy vulnerability scanner for 'eclipse-temurin:17.0.3_7-jdk-centos7' uses: aquasecurity/trivy-action@master with: - image-ref: 'eclipse-temurin:17.0.3_7-jdk-centos7 ' + image-ref: 'eclipse-temurin:17.0.3_7-jdk-centos7' format: 'sarif' output: 'trivy-results-jdk.sarif' - name: Upload Trivy scan results to GitHub Security tab for 'eclipse-temurin:17.0.3_7-jdk-centos7' From 01e411834951112d31a4fd66c8b62a04865e6a2e Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 10 Sep 2023 04:23:49 +0530 Subject: [PATCH 065/135] test security scan of JDK image --- .github/workflows/development.yml | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 640f7df..29ecc74 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -30,29 +30,29 @@ jobs: steps: - name: Clean-up run: rm -Rf * - - name: Download the application artifact - run: wget ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz --no-check-certificate - - name: Extract the artifact - run: tar -zxf ${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz - - run: mkdir ./deploy/license - - name: Download the OpenEdge License file - run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate - - name: Undeploy previous version of Sample App - run: sudo sh undeploy.sh - working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy - - name: Deploy new version of Sample App - run: sudo sh deploy.sh - working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy - - name: Run Trivy vulnerability scanner for 'sports:latest' - uses: aquasecurity/trivy-action@master - with: - image-ref: 'docker.io/sports:latest' - format: 'sarif' - output: 'trivy-results.sarif' - - name: Upload Trivy scan results to GitHub Security tab for 'sports:latest' - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: 'trivy-results.sarif' + # - name: Download the application artifact + # run: wget ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz --no-check-certificate + # - name: Extract the artifact + # run: tar -zxf ${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz + # - run: mkdir ./deploy/license + # - name: Download the OpenEdge License file + # run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate + # - name: Undeploy previous version of Sample App + # run: sudo sh undeploy.sh + # working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy + # - name: Deploy new version of Sample App + # run: sudo sh deploy.sh + # working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy + # - name: Run Trivy vulnerability scanner for 'sports:latest' + # uses: aquasecurity/trivy-action@master + # with: + # image-ref: 'docker.io/sports:latest' + # format: 'sarif' + # output: 'trivy-results.sarif' + # - name: Upload Trivy scan results to GitHub Security tab for 'sports:latest' + # uses: github/codeql-action/upload-sarif@v2 + # with: + # sarif_file: 'trivy-results.sarif' - name: Run Trivy vulnerability scanner for 'eclipse-temurin:17.0.3_7-jdk-centos7' uses: aquasecurity/trivy-action@master with: From b7d225f49eec2285bb940d3c6eafd5849ed3bcae Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 10 Sep 2023 04:33:42 +0530 Subject: [PATCH 066/135] test security scan of for sample app image --- .github/workflows/development.yml | 46 ++++++++++++------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 29ecc74..d6a3c80 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -30,39 +30,29 @@ jobs: steps: - name: Clean-up run: rm -Rf * - # - name: Download the application artifact - # run: wget ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz --no-check-certificate - # - name: Extract the artifact - # run: tar -zxf ${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz - # - run: mkdir ./deploy/license - # - name: Download the OpenEdge License file - # run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate - # - name: Undeploy previous version of Sample App - # run: sudo sh undeploy.sh - # working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy - # - name: Deploy new version of Sample App - # run: sudo sh deploy.sh - # working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy - # - name: Run Trivy vulnerability scanner for 'sports:latest' - # uses: aquasecurity/trivy-action@master - # with: - # image-ref: 'docker.io/sports:latest' - # format: 'sarif' - # output: 'trivy-results.sarif' - # - name: Upload Trivy scan results to GitHub Security tab for 'sports:latest' - # uses: github/codeql-action/upload-sarif@v2 - # with: - # sarif_file: 'trivy-results.sarif' - - name: Run Trivy vulnerability scanner for 'eclipse-temurin:17.0.3_7-jdk-centos7' + - name: Download the application artifact + run: wget ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz --no-check-certificate + - name: Extract the artifact + run: tar -zxf ${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz + - run: mkdir ./deploy/license + - name: Download the OpenEdge License file + run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate + - name: Undeploy previous version of Sample App + run: sudo sh undeploy.sh + working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy + - name: Deploy new version of Sample App + run: sudo sh deploy.sh + working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy + - name: Run security scan for Sample App docker image uses: aquasecurity/trivy-action@master with: - image-ref: 'eclipse-temurin:17.0.3_7-jdk-centos7' + image-ref: 'docker.io/sports:latest' format: 'sarif' - output: 'trivy-results-jdk.sarif' - - name: Upload Trivy scan results to GitHub Security tab for 'eclipse-temurin:17.0.3_7-jdk-centos7' + output: 'trivy-results.sarif' + - name: Upload security scan report of Sample App docker image to GitHub Security tab uses: github/codeql-action/upload-sarif@v2 with: - sarif_file: 'trivy-results-jdk.sarif' + sarif_file: 'trivy-results.sarif' report: name: Publish Reports permissions: write-all From f123b8047d17f6cfc13f00a75268ed0a8356bd59 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 13 Sep 2023 11:49:17 +0530 Subject: [PATCH 067/135] Use OEDF default plugin --- SportsApp/Sports/build.config | 2 +- SportsApp/Sports/build.gradle | 118 ++++++++++++++-------------------- 2 files changed, 49 insertions(+), 71 deletions(-) diff --git a/SportsApp/Sports/build.config b/SportsApp/Sports/build.config index 4332e47..aed9b5c 100644 --- a/SportsApp/Sports/build.config +++ b/SportsApp/Sports/build.config @@ -39,7 +39,7 @@ // The directory that will contain saved .r files. If this field is blank, .r files are saved in the // same directory as the source files. // ====================================================================================================== -buildDir="" +buildDir="build" // IDE specific configurations oeide { diff --git a/SportsApp/Sports/build.gradle b/SportsApp/Sports/build.gradle index 1eb78b9..620f344 100644 --- a/SportsApp/Sports/build.gradle +++ b/SportsApp/Sports/build.gradle @@ -6,27 +6,64 @@ * This project uses @Incubating APIs which are subject to change. */ plugins { - id("progress.openedge.abl-base") version "2.2.1" + id("progress.openedge.abl") version "2.2.1" id("base") } // Gather required variables def stgEnv = System.getProperty("STAGE_ENVIRONMENT") -def STAGE_ENVIRONMENT = stgEnv != null ? stgEnv : System.getenv("STAGE_ENVIRONMENT") +stgEnv = stgEnv != null ? stgEnv : System.getenv("STAGE_ENVIRONMENT") +def STAGE_ENVIRONMENT = stgEnv != null ? stgEnv : "dev" println "DLC: ${abl.dlcHome}" println "Stage Environment: ${STAGE_ENVIRONMENT}" +abl { + if (STAGE_ENVIRONMENT == "dev") { + dbConnection { + dbName="${buildDir}/db/sports2020/sports" + connectionParameters = "-1" + } + } else { + dbConnection { + parameterFile='conf/startup.pf' + } + } +} + +// ABL App tasks +task createSports2020(type: CreateDB){ + dbName = 'sports' + sourceDb = "${dlcHome}/sports2020" + outputDir = "${buildDir}/db/sports2020" +} +if (STAGE_ENVIRONMENT == "dev") { + compileAbl.dependsOn "createSports2020" +} + +task testABLApp(type: ABLUnit){ + source("tests") + include("**/*Suite.cls") + propath("tests", "AppServer") + outputDir = "${buildDir}/test-results/test" + arguments = [haltOnFailure: "true"] +} +if (STAGE_ENVIRONMENT == "dev") { + testABLApp.dependsOn "createSports2020" +} -// WEB App tasks -task compileWebApp(type: ABLCompile){ - source "PASOEContent/WEB-INF/openedge" - propath "PASOEContent/WEB-INF/openedge" - include "**/*.cls" - include "**/*.p" +tasks.named("compileAbl-root-AppServer"){ + rcodeDir = "${buildDir}/rcode/AppServer" +} + +tasks.named("compileAbl-root-PASOEContent-WEB-INF-openedge"){ rcodeDir = "${buildDir}/rcode/PASOEContent/WEB-INF/openedge" } +tasks.named("compileAbl-root-tests"){ + rcodeDir = "${buildDir}/rcode/tests/AppServer" +} + task packageWebApp(type: OEWar){ webAppName = "Sports" projectLocation = project.projectDir @@ -34,16 +71,9 @@ task packageWebApp(type: OEWar){ verbose = true destinationDirectory = project.distsDirectory - // exclude generated folders; - // because the ANT task used internally adds everything under the PASOEContent folder - // exclude "**/${buildDir.name}/**" - // exclude "**/output/**" - // exclude Sources from 'openedge' directory // (the ANT task used internally does it by default but added here anyway) - exclude "PASOEContent/WEB-INF/openedge/*.cls" - exclude "PASOEContent/WEB-INF/openedge/*.p" - exclude "PASOEContent/WEB-INF/openedge/*.i" + exclude "PASOEContent/WEB-INF/openedge/*.(cls|p|i)" webInf { from("${buildDir}/rcode/PASOEContent/WEB-INF/openedge") @@ -56,59 +86,7 @@ task packageWebApp(type: OEWar){ // from ("PASOEContent/META-INF/MANIFEST.MF") } } -packageWebApp.dependsOn "compileWebApp" - -// ABL App tasks -task createSports2020(type: CreateDB){ - dbName = 'sports' - sourceDb = "${dlcHome}/sports2020" - outputDir = "${buildDir}/db/sports2020" -} - -task compileABLApp(type: ABLCompile){ - source "AppServer" - propath "AppServer" - include "**/*.cls" - include "**/*.p" - rcodeDir = "${buildDir}/rcode/AppServer" - - if (STAGE_ENVIRONMENT == "prod" || STAGE_ENVIRONMENT == "staging") { - dbConnection { - parameterFile='conf/startup.pf' - } - } else { - dbConnection { - dbName="${buildDir}/db/sports2020/sports" - connectionParameters = "-1" - } - } - -} -if (STAGE_ENVIRONMENT != "prod" || STAGE_ENVIRONMENT == "staging") { - compileABLApp.dependsOn "createSports2020" -} - -task testABLApp(type: ABLUnit){ - source("tests") - include("**/*Suite.cls") - propath("tests", "AppServer") - outputDir = "${buildDir}/test-results/test" - arguments = [haltOnFailure: "true"] - - if (STAGE_ENVIRONMENT == "prod" || STAGE_ENVIRONMENT == "staging") { - dbConnection { - parameterFile='conf/startup.pf' - } - } else { - dbConnection { - dbName="${buildDir}/db/sports2020/sports" - connectionParameters = "-1" - } - } -} -if (STAGE_ENVIRONMENT != "prod" || STAGE_ENVIRONMENT == "staging") { - testABLApp.dependsOn "createSports2020" -} +packageWebApp.dependsOn "compileAbl-root-PASOEContent-WEB-INF-openedge" task packageABLApp(type: Oear){ ablAppName = "Sports" @@ -137,7 +115,7 @@ task packageABLApp(type: Oear){ // from ("conf/MANIFEST.MF") } } -packageABLApp.dependsOn "compileABLApp" +packageABLApp.dependsOn "compileAbl-root-AppServer" packageABLApp.dependsOn "packageWebApp" assemble.dependsOn "packageABLApp" From 917d6b6f9a98436153b337697bcfe80ab900a64b Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 13 Sep 2023 12:54:19 +0530 Subject: [PATCH 068/135] Sports - init project --- Sports/.dbconnection | 4 + Sports/.gitignore | 7 + Sports/.project | 29 + Sports/.propath | 13 + Sports/.services/AppServer/Customer.pidl | 870 ++++++++++++++++ Sports/.services/AppServer/Item.pidl | 635 ++++++++++++ Sports/.services/AppServer/Order.pidl | 670 +++++++++++++ Sports/.services/AppServer/Salesrep.pidl | 285 ++++++ Sports/.services/AppServer/State.pidl | 250 +++++ .../rest/SportsService/resourceModel.xml | 63 ++ .../Expose/rest/SportsService/spring.xml | 78 ++ Sports/.services/Sports.properties | 4 + Sports/.services/adapters.pamf | 392 ++++++++ .../org.eclipse.wst.common.component | 3 + ....eclipse.wst.common.project.facet.core.xml | 10 + Sports/.settings/pasoe.appserver.settings.xml | 6 + Sports/.settings/pasoe.config.xml | 4 + Sports/AppServer/Customer.cls | 253 +++++ Sports/AppServer/GetCustOrders.p | 54 + Sports/AppServer/Item.cls | 118 +++ Sports/AppServer/Order.cls | 118 +++ Sports/AppServer/Salesrep.cls | 118 +++ Sports/AppServer/State.cls | 118 +++ Sports/AppServer/UpdateCustOrders.p | 53 + Sports/AppServer/customer.i | 27 + Sports/AppServer/item.i | 44 + Sports/AppServer/myCustOrder.i | 34 + Sports/AppServer/order.i | 45 + Sports/AppServer/salesrep.i | 30 + Sports/AppServer/sports2020trgs/asordnum.p | 12 + Sports/AppServer/sports2020trgs/asstate.p | 31 + Sports/AppServer/sports2020trgs/crbin.p | 15 + Sports/AppServer/sports2020trgs/crcust.p | 15 + Sports/AppServer/sports2020trgs/cremp.p | 4 + Sports/AppServer/sports2020trgs/crintr.p | 4 + Sports/AppServer/sports2020trgs/crinv.p | 14 + Sports/AppServer/sports2020trgs/critem.p | 14 + Sports/AppServer/sports2020trgs/crlocdef.p | 4 + Sports/AppServer/sports2020trgs/crord.p | 19 + Sports/AppServer/sports2020trgs/crordl.p | 11 + Sports/AppServer/sports2020trgs/crpo.p | 4 + Sports/AppServer/sports2020trgs/crsuppl.p | 4 + Sports/AppServer/sports2020trgs/crware.p | 3 + Sports/AppServer/sports2020trgs/delcust.p | 51 + Sports/AppServer/sports2020trgs/delinv.p | 19 + Sports/AppServer/sports2020trgs/delitem.p | 7 + Sports/AppServer/sports2020trgs/delord.p | 29 + Sports/AppServer/sports2020trgs/delordl.p | 16 + Sports/AppServer/sports2020trgs/delsup.p | 28 + Sports/AppServer/sports2020trgs/ref_call.p | 13 + Sports/AppServer/sports2020trgs/wrcust.p | 52 + Sports/AppServer/sports2020trgs/writem.p | 54 + Sports/AppServer/sports2020trgs/wrord.p | 11 + Sports/AppServer/sports2020trgs/wrordl.p | 32 + Sports/AppServer/state.i | 29 + Sports/PASOEContent/META-INF/MANIFEST.MF | 8 + .../PASOEContent/WEB-INF/adapters/rest/README | 120 +++ .../rest/_oepingService/_oepingService.paar | Bin 0 -> 2871 bytes .../WEB-INF/adapters/rest/runtime.props | 21 + .../PASOEContent/WEB-INF/adapters/soap/README | 71 ++ .../WEB-INF/adapters/soap/camel-spring.xml | 21 + .../WEB-INF/adapters/web/README.txt | 34 + .../WEB-INF/backup/apsv-basic.xml | 77 ++ .../PASOEContent/WEB-INF/backup/apsv-none.xml | 14 + .../backup/oeablSecurity-anonymous.xml | 218 ++++ .../backup/oeablSecurity-basic-ldap-ext.xml | 450 +++++++++ .../backup/oeablSecurity-basic-ldap.xml | 346 +++++++ .../backup/oeablSecurity-basic-local.xml | 332 +++++++ .../backup/oeablSecurity-basic-oerealm.xml | 411 ++++++++ .../backup/oeablSecurity-basic-saml.xml | 496 ++++++++++ .../backup/oeablSecurity-container.xml | 305 ++++++ .../backup/oeablSecurity-form-ldap-ext.xml | 501 ++++++++++ .../backup/oeablSecurity-form-ldap.xml | 403 ++++++++ .../backup/oeablSecurity-form-local.xml | 385 ++++++++ .../backup/oeablSecurity-form-oerealm.xml | 455 +++++++++ .../backup/oeablSecurity-form-saml.xml | 541 ++++++++++ .../WEB-INF/backup/soap-basic.xml | 43 + .../PASOEContent/WEB-INF/backup/soap-none.xml | 14 + .../PASOEContent/WEB-INF/classes/manifest.txt | 0 .../WEB-INF/home/ServerStatus.html | 132 +++ .../WEB-INF/jsp/errorJSONBody.jsp | 54 + Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp | 39 + .../WEB-INF/jsp/errorPageBody.jsp | 54 + .../WEB-INF/jsp/errorPageFooter.jsp | 2 + .../WEB-INF/jsp/errorPageHeader.jsp | 3 + .../WEB-INF/jsp/exceptionPage.jsp | 38 + .../WEB-INF/jsp/exceptionPageFooter.jsp | 2 + .../WEB-INF/jsp/exceptionPageHeader.jsp | 3 + .../WEB-INF/jsp/httpCodeDesc-terse.properties | 25 + .../jsp/httpCodeDesc-verbose.properties | 25 + .../WEB-INF/jsp/loadErrorData.jsp | 241 +++++ Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp | 37 + .../PASOEContent/WEB-INF/jsp/logoutPage.jsp | 40 + Sports/PASOEContent/WEB-INF/logging.xml | 35 + Sports/PASOEContent/WEB-INF/metadata/README | 13 + Sports/PASOEContent/WEB-INF/oeablSecurity.csv | 59 ++ .../WEB-INF/oeablSecurity.properties | 656 ++++++++++++ .../WEB-INF/oeablSecurity.properties.README | 25 + Sports/PASOEContent/WEB-INF/oeablSecurity.xml | 70 ++ .../PASOEContent/WEB-INF/oeablSecurityJWT.csv | 49 + .../PASOEContent/WEB-INF/openedge/ReadMe.txt | 1 + Sports/PASOEContent/WEB-INF/security.tld | 195 ++++ .../WEB-INF/spring/properties-loader.xml | 38 + .../PASOEContent/WEB-INF/tlr/Merge.template | 30 + Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr | 0 Sports/PASOEContent/WEB-INF/users.properties | 4 + Sports/PASOEContent/WEB-INF/web.xml | 585 +++++++++++ .../PASOEContent/WEB-INF/web.xml-clientcert | 583 +++++++++++ Sports/PASOEContent/favicon.ico | Bin 0 -> 370070 bytes Sports/PASOEContent/index.jsp | 32 + Sports/PASOEContent/static/ServerStatus.html | 132 +++ Sports/PASOEContent/static/SportsService.json | 935 ++++++++++++++++++ Sports/PASOEContent/static/auth/login.html | 10 + Sports/PASOEContent/static/auth/login.jsp | 9 + .../PASOEContent/static/auth/loginfail.html | 6 + Sports/PASOEContent/static/auth/logout.html | 9 + Sports/PASOEContent/static/auth/logout.jsp | 11 + .../PASOEContent/static/commonPageFooter.html | 6 + .../PASOEContent/static/commonPageHeader.html | 14 + Sports/PASOEContent/static/commonStyle.css | 275 ++++++ .../PASOEContent/static/error/error401.html | 3 + .../PASOEContent/static/error/error403.html | 3 + .../PASOEContent/static/error/error404.html | 3 + .../PASOEContent/static/error/error500.html | 3 + .../PASOEContent/static/error/error503.html | 3 + Sports/PASOEContent/static/home.html | 5 + Sports/PASOEContent/static/home.jsp | 62 ++ Sports/PASOEContent/static/images/Thumbs.db | Bin 0 -> 108032 bytes .../PASOEContent/static/images/appserver.png | Bin 0 -> 1619 bytes .../PASOEContent/static/images/appserver2.png | Bin 0 -> 1627 bytes .../PASOEContent/static/images/background.png | Bin 0 -> 243375 bytes .../static/images/communities.png | Bin 0 -> 3328 bytes Sports/PASOEContent/static/images/content.png | Bin 0 -> 2510 bytes .../static/images/documentation.png | Bin 0 -> 1201 bytes Sports/PASOEContent/static/images/gear.png | Bin 0 -> 17253 bytes .../PASOEContent/static/images/progress.png | Bin 0 -> 15189 bytes Sports/PASOEContent/static/images/qstart.png | Bin 0 -> 3028 bytes Sports/PASOEContent/static/images/videos.png | Bin 0 -> 1445 bytes Sports/PASOEContent/static/index.jsp | 8 + Sports/PASOEContent/static/serverstatus.js | 78 ++ .../PASOEContent/static/sessionsExceeded.jsp | 22 + Sports/build.config | 228 +++++ Sports/conf/MANIFEST.MF | 3 + Sports/conf/startup.pf | 1 + Sports/tests/AppServer/CustomerTest.cls | 162 +++ Sports/tests/AppServer/OrderTest.cls | 43 + Sports/tests/AppServer/SportsTestSuite.cls | 16 + Sports/tlr/merge.properties | 52 + Sports/tlr/openedge.properties.merge | 2 + 149 files changed, 15270 insertions(+) create mode 100644 Sports/.dbconnection create mode 100644 Sports/.gitignore create mode 100644 Sports/.project create mode 100644 Sports/.propath create mode 100644 Sports/.services/AppServer/Customer.pidl create mode 100644 Sports/.services/AppServer/Item.pidl create mode 100644 Sports/.services/AppServer/Order.pidl create mode 100644 Sports/.services/AppServer/Salesrep.pidl create mode 100644 Sports/.services/AppServer/State.pidl create mode 100644 Sports/.services/Expose/rest/SportsService/resourceModel.xml create mode 100644 Sports/.services/Expose/rest/SportsService/spring.xml create mode 100644 Sports/.services/Sports.properties create mode 100644 Sports/.services/adapters.pamf create mode 100644 Sports/.settings/org.eclipse.wst.common.component create mode 100644 Sports/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 Sports/.settings/pasoe.appserver.settings.xml create mode 100644 Sports/.settings/pasoe.config.xml create mode 100644 Sports/AppServer/Customer.cls create mode 100644 Sports/AppServer/GetCustOrders.p create mode 100644 Sports/AppServer/Item.cls create mode 100644 Sports/AppServer/Order.cls create mode 100644 Sports/AppServer/Salesrep.cls create mode 100644 Sports/AppServer/State.cls create mode 100644 Sports/AppServer/UpdateCustOrders.p create mode 100644 Sports/AppServer/customer.i create mode 100644 Sports/AppServer/item.i create mode 100644 Sports/AppServer/myCustOrder.i create mode 100644 Sports/AppServer/order.i create mode 100644 Sports/AppServer/salesrep.i create mode 100644 Sports/AppServer/sports2020trgs/asordnum.p create mode 100644 Sports/AppServer/sports2020trgs/asstate.p create mode 100644 Sports/AppServer/sports2020trgs/crbin.p create mode 100644 Sports/AppServer/sports2020trgs/crcust.p create mode 100644 Sports/AppServer/sports2020trgs/cremp.p create mode 100644 Sports/AppServer/sports2020trgs/crintr.p create mode 100644 Sports/AppServer/sports2020trgs/crinv.p create mode 100644 Sports/AppServer/sports2020trgs/critem.p create mode 100644 Sports/AppServer/sports2020trgs/crlocdef.p create mode 100644 Sports/AppServer/sports2020trgs/crord.p create mode 100644 Sports/AppServer/sports2020trgs/crordl.p create mode 100644 Sports/AppServer/sports2020trgs/crpo.p create mode 100644 Sports/AppServer/sports2020trgs/crsuppl.p create mode 100644 Sports/AppServer/sports2020trgs/crware.p create mode 100644 Sports/AppServer/sports2020trgs/delcust.p create mode 100644 Sports/AppServer/sports2020trgs/delinv.p create mode 100644 Sports/AppServer/sports2020trgs/delitem.p create mode 100644 Sports/AppServer/sports2020trgs/delord.p create mode 100644 Sports/AppServer/sports2020trgs/delordl.p create mode 100644 Sports/AppServer/sports2020trgs/delsup.p create mode 100644 Sports/AppServer/sports2020trgs/ref_call.p create mode 100644 Sports/AppServer/sports2020trgs/wrcust.p create mode 100644 Sports/AppServer/sports2020trgs/writem.p create mode 100644 Sports/AppServer/sports2020trgs/wrord.p create mode 100644 Sports/AppServer/sports2020trgs/wrordl.p create mode 100644 Sports/AppServer/state.i create mode 100644 Sports/PASOEContent/META-INF/MANIFEST.MF create mode 100644 Sports/PASOEContent/WEB-INF/adapters/rest/README create mode 100644 Sports/PASOEContent/WEB-INF/adapters/rest/_oepingService/_oepingService.paar create mode 100644 Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props create mode 100644 Sports/PASOEContent/WEB-INF/adapters/soap/README create mode 100644 Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml create mode 100644 Sports/PASOEContent/WEB-INF/adapters/web/README.txt create mode 100644 Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/apsv-none.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/soap-basic.xml create mode 100644 Sports/PASOEContent/WEB-INF/backup/soap-none.xml create mode 100644 Sports/PASOEContent/WEB-INF/classes/manifest.txt create mode 100644 Sports/PASOEContent/WEB-INF/home/ServerStatus.html create mode 100644 Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp create mode 100644 Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp create mode 100644 Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp create mode 100644 Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp create mode 100644 Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp create mode 100644 Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp create mode 100644 Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp create mode 100644 Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp create mode 100644 Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties create mode 100644 Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties create mode 100644 Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp create mode 100644 Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp create mode 100644 Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp create mode 100644 Sports/PASOEContent/WEB-INF/logging.xml create mode 100644 Sports/PASOEContent/WEB-INF/metadata/README create mode 100644 Sports/PASOEContent/WEB-INF/oeablSecurity.csv create mode 100644 Sports/PASOEContent/WEB-INF/oeablSecurity.properties create mode 100644 Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README create mode 100644 Sports/PASOEContent/WEB-INF/oeablSecurity.xml create mode 100644 Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv create mode 100644 Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt create mode 100644 Sports/PASOEContent/WEB-INF/security.tld create mode 100644 Sports/PASOEContent/WEB-INF/spring/properties-loader.xml create mode 100644 Sports/PASOEContent/WEB-INF/tlr/Merge.template create mode 100644 Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr create mode 100644 Sports/PASOEContent/WEB-INF/users.properties create mode 100644 Sports/PASOEContent/WEB-INF/web.xml create mode 100644 Sports/PASOEContent/WEB-INF/web.xml-clientcert create mode 100644 Sports/PASOEContent/favicon.ico create mode 100644 Sports/PASOEContent/index.jsp create mode 100644 Sports/PASOEContent/static/ServerStatus.html create mode 100644 Sports/PASOEContent/static/SportsService.json create mode 100644 Sports/PASOEContent/static/auth/login.html create mode 100644 Sports/PASOEContent/static/auth/login.jsp create mode 100644 Sports/PASOEContent/static/auth/loginfail.html create mode 100644 Sports/PASOEContent/static/auth/logout.html create mode 100644 Sports/PASOEContent/static/auth/logout.jsp create mode 100644 Sports/PASOEContent/static/commonPageFooter.html create mode 100644 Sports/PASOEContent/static/commonPageHeader.html create mode 100644 Sports/PASOEContent/static/commonStyle.css create mode 100644 Sports/PASOEContent/static/error/error401.html create mode 100644 Sports/PASOEContent/static/error/error403.html create mode 100644 Sports/PASOEContent/static/error/error404.html create mode 100644 Sports/PASOEContent/static/error/error500.html create mode 100644 Sports/PASOEContent/static/error/error503.html create mode 100644 Sports/PASOEContent/static/home.html create mode 100644 Sports/PASOEContent/static/home.jsp create mode 100644 Sports/PASOEContent/static/images/Thumbs.db create mode 100644 Sports/PASOEContent/static/images/appserver.png create mode 100644 Sports/PASOEContent/static/images/appserver2.png create mode 100644 Sports/PASOEContent/static/images/background.png create mode 100644 Sports/PASOEContent/static/images/communities.png create mode 100644 Sports/PASOEContent/static/images/content.png create mode 100644 Sports/PASOEContent/static/images/documentation.png create mode 100644 Sports/PASOEContent/static/images/gear.png create mode 100644 Sports/PASOEContent/static/images/progress.png create mode 100644 Sports/PASOEContent/static/images/qstart.png create mode 100644 Sports/PASOEContent/static/images/videos.png create mode 100644 Sports/PASOEContent/static/index.jsp create mode 100644 Sports/PASOEContent/static/serverstatus.js create mode 100644 Sports/PASOEContent/static/sessionsExceeded.jsp create mode 100644 Sports/build.config create mode 100644 Sports/conf/MANIFEST.MF create mode 100644 Sports/conf/startup.pf create mode 100644 Sports/tests/AppServer/CustomerTest.cls create mode 100644 Sports/tests/AppServer/OrderTest.cls create mode 100644 Sports/tests/AppServer/SportsTestSuite.cls create mode 100644 Sports/tlr/merge.properties create mode 100644 Sports/tlr/openedge.properties.merge diff --git a/Sports/.dbconnection b/Sports/.dbconnection new file mode 100644 index 0000000..5c94f99 --- /dev/null +++ b/Sports/.dbconnection @@ -0,0 +1,4 @@ + + + + diff --git a/Sports/.gitignore b/Sports/.gitignore new file mode 100644 index 0000000..0f0ebf3 --- /dev/null +++ b/Sports/.gitignore @@ -0,0 +1,7 @@ +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build +build-output +results.xml \ No newline at end of file diff --git a/Sports/.project b/Sports/.project new file mode 100644 index 0000000..138fd72 --- /dev/null +++ b/Sports/.project @@ -0,0 +1,29 @@ + + + Sports + + + + + + com.openedge.pdt.text.progressBuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + + com.openedge.pdt.text.progressNature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.common.modulecore.ModuleCoreNature + + diff --git a/Sports/.propath b/Sports/.propath new file mode 100644 index 0000000..5ddc5cc --- /dev/null +++ b/Sports/.propath @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Sports/.services/AppServer/Customer.pidl b/Sports/.services/AppServer/Customer.pidl new file mode 100644 index 0000000..92093e4 --- /dev/null +++ b/Sports/.services/AppServer/Customer.pidl @@ -0,0 +1,870 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Customer + + + cls + + Customer + + + count + 1 + + count + + filter + filter + 1 + 1 + + + numRecs + numRecs + 4 + 2 + + + + + CreateCustomer + 1 + + CreateCustomer + + dsCustomer + dsCustomer + 36 + 3 + + + + + id + 1 + 0 + CHARACTER + 0 + + + seq + 4 + 0 + INTEGER + 0 + + + CustNum + 4 + 0 + INTEGER + 0 + + + Name + 1 + 0 + CHARACTER + 0 + + + Address + 1 + 0 + CHARACTER + 0 + + + Address2 + 1 + 0 + CHARACTER + 0 + + + Balance + 5 + 0 + DECIMAL + 0 + + + City + 1 + 0 + CHARACTER + 0 + + + Comments + 1 + 0 + CHARACTER + 0 + + + Contact + 1 + 0 + CHARACTER + 0 + + + Country + 1 + 0 + CHARACTER + 0 + + + CreditLimit + 5 + 0 + DECIMAL + 0 + + + Discount + 4 + 0 + INTEGER + 0 + + + EmailAddress + 1 + 0 + CHARACTER + 0 + + + Fax + 1 + 0 + CHARACTER + 0 + + + Phone + 1 + 0 + CHARACTER + 0 + + + PostalCode + 1 + 0 + CHARACTER + 0 + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + State + 1 + 0 + CHARACTER + 0 + + + Terms + 1 + 0 + CHARACTER + 0 + + + + + + + + DeleteCustomer + 1 + + DeleteCustomer + + dsCustomer + dsCustomer + 36 + 3 + + + + + id + 1 + 0 + CHARACTER + 0 + + + seq + 4 + 0 + INTEGER + 0 + + + CustNum + 4 + 0 + INTEGER + 0 + + + Name + 1 + 0 + CHARACTER + 0 + + + Address + 1 + 0 + CHARACTER + 0 + + + Address2 + 1 + 0 + CHARACTER + 0 + + + Balance + 5 + 0 + DECIMAL + 0 + + + City + 1 + 0 + CHARACTER + 0 + + + Comments + 1 + 0 + CHARACTER + 0 + + + Contact + 1 + 0 + CHARACTER + 0 + + + Country + 1 + 0 + CHARACTER + 0 + + + CreditLimit + 5 + 0 + DECIMAL + 0 + + + Discount + 4 + 0 + INTEGER + 0 + + + EmailAddress + 1 + 0 + CHARACTER + 0 + + + Fax + 1 + 0 + CHARACTER + 0 + + + Phone + 1 + 0 + CHARACTER + 0 + + + PostalCode + 1 + 0 + CHARACTER + 0 + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + State + 1 + 0 + CHARACTER + 0 + + + Terms + 1 + 0 + CHARACTER + 0 + + + + + + + + ReadCustomer + 1 + + ReadCustomer + + filter + filter + 1 + 1 + + + dsCustomer + dsCustomer + 36 + 2 + + + + + id + 1 + 0 + CHARACTER + 0 + + + seq + 4 + 0 + INTEGER + 0 + + + CustNum + 4 + 0 + INTEGER + 0 + + + Name + 1 + 0 + CHARACTER + 0 + + + Address + 1 + 0 + CHARACTER + 0 + + + Address2 + 1 + 0 + CHARACTER + 0 + + + Balance + 5 + 0 + DECIMAL + 0 + + + City + 1 + 0 + CHARACTER + 0 + + + Comments + 1 + 0 + CHARACTER + 0 + + + Contact + 1 + 0 + CHARACTER + 0 + + + Country + 1 + 0 + CHARACTER + 0 + + + CreditLimit + 5 + 0 + DECIMAL + 0 + + + Discount + 4 + 0 + INTEGER + 0 + + + EmailAddress + 1 + 0 + CHARACTER + 0 + + + Fax + 1 + 0 + CHARACTER + 0 + + + Phone + 1 + 0 + CHARACTER + 0 + + + PostalCode + 1 + 0 + CHARACTER + 0 + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + State + 1 + 0 + CHARACTER + 0 + + + Terms + 1 + 0 + CHARACTER + 0 + + + + + + + + SubmitCustomer + 1 + + SubmitCustomer + + dsCustomer + dsCustomer + 36 + 3 + + + + + id + 1 + 0 + CHARACTER + 0 + + + seq + 4 + 0 + INTEGER + 0 + + + CustNum + 4 + 0 + INTEGER + 0 + + + Name + 1 + 0 + CHARACTER + 0 + + + Address + 1 + 0 + CHARACTER + 0 + + + Address2 + 1 + 0 + CHARACTER + 0 + + + Balance + 5 + 0 + DECIMAL + 0 + + + City + 1 + 0 + CHARACTER + 0 + + + Comments + 1 + 0 + CHARACTER + 0 + + + Contact + 1 + 0 + CHARACTER + 0 + + + Country + 1 + 0 + CHARACTER + 0 + + + CreditLimit + 5 + 0 + DECIMAL + 0 + + + Discount + 4 + 0 + INTEGER + 0 + + + EmailAddress + 1 + 0 + CHARACTER + 0 + + + Fax + 1 + 0 + CHARACTER + 0 + + + Phone + 1 + 0 + CHARACTER + 0 + + + PostalCode + 1 + 0 + CHARACTER + 0 + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + State + 1 + 0 + CHARACTER + 0 + + + Terms + 1 + 0 + CHARACTER + 0 + + + + + + + + UpdateCustomer + 1 + + UpdateCustomer + + dsCustomer + dsCustomer + 36 + 3 + + + + + id + 1 + 0 + CHARACTER + 0 + + + seq + 4 + 0 + INTEGER + 0 + + + CustNum + 4 + 0 + INTEGER + 0 + + + Name + 1 + 0 + CHARACTER + 0 + + + Address + 1 + 0 + CHARACTER + 0 + + + Address2 + 1 + 0 + CHARACTER + 0 + + + Balance + 5 + 0 + DECIMAL + 0 + + + City + 1 + 0 + CHARACTER + 0 + + + Comments + 1 + 0 + CHARACTER + 0 + + + Contact + 1 + 0 + CHARACTER + 0 + + + Country + 1 + 0 + CHARACTER + 0 + + + CreditLimit + 5 + 0 + DECIMAL + 0 + + + Discount + 4 + 0 + INTEGER + 0 + + + EmailAddress + 1 + 0 + CHARACTER + 0 + + + Fax + 1 + 0 + CHARACTER + 0 + + + Phone + 1 + 0 + CHARACTER + 0 + + + PostalCode + 1 + 0 + CHARACTER + 0 + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + State + 1 + 0 + CHARACTER + 0 + + + Terms + 1 + 0 + CHARACTER + 0 + + + + + + + + + + diff --git a/Sports/.services/AppServer/Item.pidl b/Sports/.services/AppServer/Item.pidl new file mode 100644 index 0000000..4ad7017 --- /dev/null +++ b/Sports/.services/AppServer/Item.pidl @@ -0,0 +1,635 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Item + + + cls + + Item + + + CreateItem + 1 + + CreateItem + + dsItem + dsItem + 36 + 3 + + + + + Itemnum + 4 + 0 + INTEGER + 0 + + + ItemName + 1 + 0 + CHARACTER + 0 + + + Price + 5 + 0 + DECIMAL + 0 + + + Onhand + 4 + 0 + INTEGER + 0 + + + Allocated + 4 + 0 + INTEGER + 0 + + + ReOrder + 4 + 0 + INTEGER + 0 + + + OnOrder + 4 + 0 + INTEGER + 0 + + + CatPage + 4 + 0 + INTEGER + 0 + + + CatDescription + 1 + 0 + CHARACTER + 0 + + + Category1 + 1 + 0 + CHARACTER + 0 + + + Category2 + 1 + 0 + CHARACTER + 0 + + + Special + 1 + 0 + CHARACTER + 0 + + + Weight + 5 + 0 + DECIMAL + 0 + + + Minqty + 4 + 0 + INTEGER + 0 + + + + + + + + DeleteItem + 1 + + DeleteItem + + dsItem + dsItem + 36 + 3 + + + + + Itemnum + 4 + 0 + INTEGER + 0 + + + ItemName + 1 + 0 + CHARACTER + 0 + + + Price + 5 + 0 + DECIMAL + 0 + + + Onhand + 4 + 0 + INTEGER + 0 + + + Allocated + 4 + 0 + INTEGER + 0 + + + ReOrder + 4 + 0 + INTEGER + 0 + + + OnOrder + 4 + 0 + INTEGER + 0 + + + CatPage + 4 + 0 + INTEGER + 0 + + + CatDescription + 1 + 0 + CHARACTER + 0 + + + Category1 + 1 + 0 + CHARACTER + 0 + + + Category2 + 1 + 0 + CHARACTER + 0 + + + Special + 1 + 0 + CHARACTER + 0 + + + Weight + 5 + 0 + DECIMAL + 0 + + + Minqty + 4 + 0 + INTEGER + 0 + + + + + + + + ReadItem + 1 + + ReadItem + + filter + filter + 1 + 1 + + + dsItem + dsItem + 36 + 2 + + + + + Itemnum + 4 + 0 + INTEGER + 0 + + + ItemName + 1 + 0 + CHARACTER + 0 + + + Price + 5 + 0 + DECIMAL + 0 + + + Onhand + 4 + 0 + INTEGER + 0 + + + Allocated + 4 + 0 + INTEGER + 0 + + + ReOrder + 4 + 0 + INTEGER + 0 + + + OnOrder + 4 + 0 + INTEGER + 0 + + + CatPage + 4 + 0 + INTEGER + 0 + + + CatDescription + 1 + 0 + CHARACTER + 0 + + + Category1 + 1 + 0 + CHARACTER + 0 + + + Category2 + 1 + 0 + CHARACTER + 0 + + + Special + 1 + 0 + CHARACTER + 0 + + + Weight + 5 + 0 + DECIMAL + 0 + + + Minqty + 4 + 0 + INTEGER + 0 + + + + + + + + SubmitItem + 1 + + SubmitItem + + dsItem + dsItem + 36 + 3 + + + + + Itemnum + 4 + 0 + INTEGER + 0 + + + ItemName + 1 + 0 + CHARACTER + 0 + + + Price + 5 + 0 + DECIMAL + 0 + + + Onhand + 4 + 0 + INTEGER + 0 + + + Allocated + 4 + 0 + INTEGER + 0 + + + ReOrder + 4 + 0 + INTEGER + 0 + + + OnOrder + 4 + 0 + INTEGER + 0 + + + CatPage + 4 + 0 + INTEGER + 0 + + + CatDescription + 1 + 0 + CHARACTER + 0 + + + Category1 + 1 + 0 + CHARACTER + 0 + + + Category2 + 1 + 0 + CHARACTER + 0 + + + Special + 1 + 0 + CHARACTER + 0 + + + Weight + 5 + 0 + DECIMAL + 0 + + + Minqty + 4 + 0 + INTEGER + 0 + + + + + + + + UpdateItem + 1 + + UpdateItem + + dsItem + dsItem + 36 + 3 + + + + + Itemnum + 4 + 0 + INTEGER + 0 + + + ItemName + 1 + 0 + CHARACTER + 0 + + + Price + 5 + 0 + DECIMAL + 0 + + + Onhand + 4 + 0 + INTEGER + 0 + + + Allocated + 4 + 0 + INTEGER + 0 + + + ReOrder + 4 + 0 + INTEGER + 0 + + + OnOrder + 4 + 0 + INTEGER + 0 + + + CatPage + 4 + 0 + INTEGER + 0 + + + CatDescription + 1 + 0 + CHARACTER + 0 + + + Category1 + 1 + 0 + CHARACTER + 0 + + + Category2 + 1 + 0 + CHARACTER + 0 + + + Special + 1 + 0 + CHARACTER + 0 + + + Weight + 5 + 0 + DECIMAL + 0 + + + Minqty + 4 + 0 + INTEGER + 0 + + + + + + + + + + diff --git a/Sports/.services/AppServer/Order.pidl b/Sports/.services/AppServer/Order.pidl new file mode 100644 index 0000000..a24b84f --- /dev/null +++ b/Sports/.services/AppServer/Order.pidl @@ -0,0 +1,670 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Order + + + cls + + Order + + + CreateOrder + 1 + + CreateOrder + + dsOrder + dsOrder + 36 + 3 + + + + + Ordernum + 4 + 0 + INTEGER + 0 + + + CustNum + 4 + 0 + INTEGER + 0 + + + OrderDate + 2 + 0 + DATE + 0 + + + ShipDate + 2 + 0 + DATE + 0 + + + PromiseDate + 2 + 0 + DATE + 0 + + + Carrier + 1 + 0 + CHARACTER + 0 + + + Instructions + 1 + 0 + CHARACTER + 0 + + + PO + 1 + 0 + CHARACTER + 0 + + + Terms + 1 + 0 + CHARACTER + 0 + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + BillToID + 4 + 0 + INTEGER + 0 + + + ShipToID + 4 + 0 + INTEGER + 0 + + + OrderStatus + 1 + 0 + CHARACTER + 0 + + + WarehouseNum + 4 + 0 + INTEGER + 0 + + + Creditcard + 1 + 0 + CHARACTER + 0 + + + + + + + + DeleteOrder + 1 + + DeleteOrder + + dsOrder + dsOrder + 36 + 3 + + + + + Ordernum + 4 + 0 + INTEGER + 0 + + + CustNum + 4 + 0 + INTEGER + 0 + + + OrderDate + 2 + 0 + DATE + 0 + + + ShipDate + 2 + 0 + DATE + 0 + + + PromiseDate + 2 + 0 + DATE + 0 + + + Carrier + 1 + 0 + CHARACTER + 0 + + + Instructions + 1 + 0 + CHARACTER + 0 + + + PO + 1 + 0 + CHARACTER + 0 + + + Terms + 1 + 0 + CHARACTER + 0 + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + BillToID + 4 + 0 + INTEGER + 0 + + + ShipToID + 4 + 0 + INTEGER + 0 + + + OrderStatus + 1 + 0 + CHARACTER + 0 + + + WarehouseNum + 4 + 0 + INTEGER + 0 + + + Creditcard + 1 + 0 + CHARACTER + 0 + + + + + + + + ReadOrder + 1 + + ReadOrder + + filter + filter + 1 + 1 + + + dsOrder + dsOrder + 36 + 2 + + + + + Ordernum + 4 + 0 + INTEGER + 0 + + + CustNum + 4 + 0 + INTEGER + 0 + + + OrderDate + 2 + 0 + DATE + 0 + + + ShipDate + 2 + 0 + DATE + 0 + + + PromiseDate + 2 + 0 + DATE + 0 + + + Carrier + 1 + 0 + CHARACTER + 0 + + + Instructions + 1 + 0 + CHARACTER + 0 + + + PO + 1 + 0 + CHARACTER + 0 + + + Terms + 1 + 0 + CHARACTER + 0 + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + BillToID + 4 + 0 + INTEGER + 0 + + + ShipToID + 4 + 0 + INTEGER + 0 + + + OrderStatus + 1 + 0 + CHARACTER + 0 + + + WarehouseNum + 4 + 0 + INTEGER + 0 + + + Creditcard + 1 + 0 + CHARACTER + 0 + + + + + + + + SubmitOrder + 1 + + SubmitOrder + + dsOrder + dsOrder + 36 + 3 + + + + + Ordernum + 4 + 0 + INTEGER + 0 + + + CustNum + 4 + 0 + INTEGER + 0 + + + OrderDate + 2 + 0 + DATE + 0 + + + ShipDate + 2 + 0 + DATE + 0 + + + PromiseDate + 2 + 0 + DATE + 0 + + + Carrier + 1 + 0 + CHARACTER + 0 + + + Instructions + 1 + 0 + CHARACTER + 0 + + + PO + 1 + 0 + CHARACTER + 0 + + + Terms + 1 + 0 + CHARACTER + 0 + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + BillToID + 4 + 0 + INTEGER + 0 + + + ShipToID + 4 + 0 + INTEGER + 0 + + + OrderStatus + 1 + 0 + CHARACTER + 0 + + + WarehouseNum + 4 + 0 + INTEGER + 0 + + + Creditcard + 1 + 0 + CHARACTER + 0 + + + + + + + + UpdateOrder + 1 + + UpdateOrder + + dsOrder + dsOrder + 36 + 3 + + + + + Ordernum + 4 + 0 + INTEGER + 0 + + + CustNum + 4 + 0 + INTEGER + 0 + + + OrderDate + 2 + 0 + DATE + 0 + + + ShipDate + 2 + 0 + DATE + 0 + + + PromiseDate + 2 + 0 + DATE + 0 + + + Carrier + 1 + 0 + CHARACTER + 0 + + + Instructions + 1 + 0 + CHARACTER + 0 + + + PO + 1 + 0 + CHARACTER + 0 + + + Terms + 1 + 0 + CHARACTER + 0 + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + BillToID + 4 + 0 + INTEGER + 0 + + + ShipToID + 4 + 0 + INTEGER + 0 + + + OrderStatus + 1 + 0 + CHARACTER + 0 + + + WarehouseNum + 4 + 0 + INTEGER + 0 + + + Creditcard + 1 + 0 + CHARACTER + 0 + + + + + + + + + + diff --git a/Sports/.services/AppServer/Salesrep.pidl b/Sports/.services/AppServer/Salesrep.pidl new file mode 100644 index 0000000..c7e5253 --- /dev/null +++ b/Sports/.services/AppServer/Salesrep.pidl @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Salesrep + + + cls + + Salesrep + + + CreateSalesrep + 1 + + CreateSalesrep + + dsSalesrep + dsSalesrep + 36 + 3 + + + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + RepName + 1 + 0 + CHARACTER + 0 + + + Region + 1 + 0 + CHARACTER + 0 + + + MonthQuota + 4 + 12 + INTEGER + 0 + + + + + + + + DeleteSalesrep + 1 + + DeleteSalesrep + + dsSalesrep + dsSalesrep + 36 + 3 + + + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + RepName + 1 + 0 + CHARACTER + 0 + + + Region + 1 + 0 + CHARACTER + 0 + + + MonthQuota + 4 + 12 + INTEGER + 0 + + + + + + + + ReadSalesrep + 1 + + ReadSalesrep + + filter + filter + 1 + 1 + + + dsSalesrep + dsSalesrep + 36 + 2 + + + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + RepName + 1 + 0 + CHARACTER + 0 + + + Region + 1 + 0 + CHARACTER + 0 + + + MonthQuota + 4 + 12 + INTEGER + 0 + + + + + + + + SubmitSalesrep + 1 + + SubmitSalesrep + + dsSalesrep + dsSalesrep + 36 + 3 + + + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + RepName + 1 + 0 + CHARACTER + 0 + + + Region + 1 + 0 + CHARACTER + 0 + + + MonthQuota + 4 + 12 + INTEGER + 0 + + + + + + + + UpdateSalesrep + 1 + + UpdateSalesrep + + dsSalesrep + dsSalesrep + 36 + 3 + + + + + SalesRep + 1 + 0 + CHARACTER + 0 + + + RepName + 1 + 0 + CHARACTER + 0 + + + Region + 1 + 0 + CHARACTER + 0 + + + MonthQuota + 4 + 12 + INTEGER + 0 + + + + + + + + + + diff --git a/Sports/.services/AppServer/State.pidl b/Sports/.services/AppServer/State.pidl new file mode 100644 index 0000000..68da206 --- /dev/null +++ b/Sports/.services/AppServer/State.pidl @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + State + + + cls + + State + + + CreateState + 1 + + CreateState + + dsState + dsState + 36 + 3 + + + + + State + 1 + 0 + CHARACTER + 0 + + + StateName + 1 + 0 + CHARACTER + 0 + + + Region + 1 + 0 + CHARACTER + 0 + + + + + + + + DeleteState + 1 + + DeleteState + + dsState + dsState + 36 + 3 + + + + + State + 1 + 0 + CHARACTER + 0 + + + StateName + 1 + 0 + CHARACTER + 0 + + + Region + 1 + 0 + CHARACTER + 0 + + + + + + + + ReadState + 1 + + ReadState + + filter + filter + 1 + 1 + + + dsState + dsState + 36 + 2 + + + + + State + 1 + 0 + CHARACTER + 0 + + + StateName + 1 + 0 + CHARACTER + 0 + + + Region + 1 + 0 + CHARACTER + 0 + + + + + + + + SubmitState + 1 + + SubmitState + + dsState + dsState + 36 + 3 + + + + + State + 1 + 0 + CHARACTER + 0 + + + StateName + 1 + 0 + CHARACTER + 0 + + + Region + 1 + 0 + CHARACTER + 0 + + + + + + + + UpdateState + 1 + + UpdateState + + dsState + dsState + 36 + 3 + + + + + State + 1 + 0 + CHARACTER + 0 + + + StateName + 1 + 0 + CHARACTER + 0 + + + Region + 1 + 0 + CHARACTER + 0 + + + + + + + + + + diff --git a/Sports/.services/Expose/rest/SportsService/resourceModel.xml b/Sports/.services/Expose/rest/SportsService/resourceModel.xml new file mode 100644 index 0000000..8b6c5ad --- /dev/null +++ b/Sports/.services/Expose/rest/SportsService/resourceModel.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/.services/Expose/rest/SportsService/spring.xml b/Sports/.services/Expose/rest/SportsService/spring.xml new file mode 100644 index 0000000..0901f25 --- /dev/null +++ b/Sports/.services/Expose/rest/SportsService/spring.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.progress.rest.adapters.oe.Open4GLException + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/.services/Sports.properties b/Sports/.services/Sports.properties new file mode 100644 index 0000000..e259010 --- /dev/null +++ b/Sports/.services/Sports.properties @@ -0,0 +1,4 @@ +#Properties generated using Progress Tooling +#Wed Feb 06 14:18:49 IST 2019 +Spring.XmlFile.Extensions=xml +archiveName=Sports diff --git a/Sports/.services/adapters.pamf b/Sports/.services/adapters.pamf new file mode 100644 index 0000000..af6713b --- /dev/null +++ b/Sports/.services/adapters.pamf @@ -0,0 +1,392 @@ + + + + + + + + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsCustomer']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsCustomer']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsCustomer']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsCustomer']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsCustomer']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsCustomer']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsCustomer']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsCustomer']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsCustomer']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['numRecs']}" target="${json.object['response'].integervalue['numRecs']}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsItem']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsItem']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsItem']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsItem']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsItem']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsItem']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsItem']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsItem']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsItem']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsOrder']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsOrder']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsOrder']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsOrder']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsOrder']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsOrder']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsOrder']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsOrder']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsOrder']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsSalesrep']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsSalesrep']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsSalesrep']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsSalesrep']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsSalesrep']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsSalesrep']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsSalesrep']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsSalesrep']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsSalesrep']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsState']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsState']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsState']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsState']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsState']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsState']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsState']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + <?xml version="1.0" encoding="UTF-8"?> +<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> + <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> + <metadata:mapInput/> + <metadata:mapOutput/> + </metadata:toolingMetadata> + <mapping:mapInput> + <mapping:rule source="${http.body}" target="${idl.param['dsState']}"/> + </mapping:mapInput> + <mapping:mapOutput> + <mapping:rule source="${idl.param['dsState']}" target="${http.body}"/> + </mapping:mapOutput> + <mapping:mapFault name="default_fault"/> +</mapping:messageMapping> + /SportsService + + + + + <?xml version="1.0" encoding="UTF-8" standalone="no"?><oe_mobile><service><name>SportsService</name><resources><path>AppServer/Customer.cls</path><path>AppServer/Item.cls</path><path>AppServer/Order.cls</path><path>AppServer/Salesrep.cls</path><path>AppServer/State.cls</path></resources></service></oe_mobile> + + + + + + + + + + + + + Sports.properties + + diff --git a/Sports/.settings/org.eclipse.wst.common.component b/Sports/.settings/org.eclipse.wst.common.component new file mode 100644 index 0000000..8f2d00d --- /dev/null +++ b/Sports/.settings/org.eclipse.wst.common.component @@ -0,0 +1,3 @@ + + + diff --git a/Sports/.settings/org.eclipse.wst.common.project.facet.core.xml b/Sports/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 0000000..53435d4 --- /dev/null +++ b/Sports/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Sports/.settings/pasoe.appserver.settings.xml b/Sports/.settings/pasoe.appserver.settings.xml new file mode 100644 index 0000000..862d40e --- /dev/null +++ b/Sports/.settings/pasoe.appserver.settings.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/Sports/.settings/pasoe.config.xml b/Sports/.settings/pasoe.config.xml new file mode 100644 index 0000000..91613ca --- /dev/null +++ b/Sports/.settings/pasoe.config.xml @@ -0,0 +1,4 @@ + + + Sports + diff --git a/Sports/AppServer/Customer.cls b/Sports/AppServer/Customer.cls new file mode 100644 index 0000000..692df17 --- /dev/null +++ b/Sports/AppServer/Customer.cls @@ -0,0 +1,253 @@ +@program FILE(name="Customer.cls", module="AppServer"). +@openapi.openedge.export FILE(type="REST", executionMode="singleton", useReturnValue="false", writeDataSetBeforeImage="false"). +@progress.service.resource FILE(name="Customer", URI="/Customer", schemaName="dsCustomer", schemaFile="Sports/AppServer/customer.i"). + +USING Progress.Lang.*. + +USING OpenEdge.BusinessLogic.BusinessEntity. +USING Progress.Json.ObjectModel.*. + +BLOCK-LEVEL ON ERROR UNDO, THROW. + +CLASS Customer INHERITS BusinessEntity: + + {"customer.i"} + + DEFINE DATA-SOURCE srcCustomer FOR Customer. + + DEFINE VARIABLE iSeq AS INTEGER NO-UNDO. + + CONSTRUCTOR PUBLIC Customer(): + + DEFINE VAR hDataSourceArray AS HANDLE NO-UNDO EXTENT 1. + DEFINE VAR cSkipListArray AS CHAR NO-UNDO EXTENT 1. + + SUPER (DATASET dsCustomer:HANDLE). + + /* Data Source for each table in dataset. + Should be in table order as defined in DataSet */ + hDataSourceArray[1] = DATA-SOURCE srcCustomer:HANDLE. + + /* Skip-list entry array for each table in DataSet. + Should be in temp-table order as defined in DataSet */ + /* Each skip-list entry is a comma-separated list of field names + to be ignored in the CREATE statement */ + + cSkipListArray[1] = "CustNum". + + THIS-OBJECT:ProDataSource = hDataSourceArray. + THIS-OBJECT:SkipList = cSkipListArray. + + END CONSTRUCTOR. + + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="read", URI="?filter=~{filter~}", alias="", mediaType="application/json"). + @openapi.openedge.method.property (name="mappingType", value="JFP"). + @openapi.openedge.method.property (name="capabilities", value="ablFilter,top,skip,id,orderBy"). + METHOD PUBLIC VOID ReadCustomer( + INPUT filter AS CHARACTER, + OUTPUT DATASET dsCustomer): + + IF filter BEGINS "~{" THEN + THIS-OBJECT:JFPFillMethod (INPUT filter). + ELSE + DO: + BUFFER ttCustomer:HANDLE:BATCH-SIZE = 0. + BUFFER ttCustomer:SET-CALLBACK ("AFTER-ROW-FILL", "AddIdField"). + SUPER:ReadData(filter). + END. + END METHOD. + + /* Other CUD and Submit operation methods */ + + /*------------------------------------------------------------------------------ + Purpose: Create one or more new records + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="create", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID CreateCustomer(INPUT-OUTPUT DATASET dsCustomer): + + SUPER:CreateData(DATASET dsCustomer BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Update one or more records + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="update", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID UpdateCustomer(INPUT-OUTPUT DATASET dsCustomer): + + SUPER:UpdateData(DATASET dsCustomer BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Delete a record + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="delete", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID DeleteCustomer(INPUT-OUTPUT DATASET dsCustomer): + + SUPER:DeleteData(DATASET dsCustomer BY-REFERENCE). + END METHOD. + + + METHOD PRIVATE VOID JFPFillMethod(INPUT filter AS CHARACTER): + + DEFINE VARIABLE jsonParser AS ObjectModelParser NO-UNDO. + DEFINE VARIABLE jsonObject AS JsonObject NO-UNDO. + DEFINE VARIABLE cWhere AS CHARACTER NO-UNDO. + DEFINE VARIABLE hQuery AS HANDLE NO-UNDO. + DEFINE VARIABLE lUseReposition AS LOGICAL NO-UNDO. + DEFINE VARIABLE iCount AS INTEGER NO-UNDO. + DEFINE VARIABLE ablFilter AS CHARACTER NO-UNDO. + DEFINE VARIABLE id AS CHARACTER INITIAL ? NO-UNDO. + DEFINE VARIABLE iMaxRows AS INTEGER INITIAL ? NO-UNDO. + DEFINE VARIABLE iSkipRows AS INTEGER INITIAL ? NO-UNDO. + DEFINE VARIABLE cOrderBy AS CHARACTER INITIAL "" NO-UNDO. + + /* purge any existing data */ + EMPTY TEMP-TABLE ttCustomer. + + jsonParser = NEW ObjectModelParser(). + jsonObject = CAST(jsonParser:Parse(filter), jsonObject). + iMaxRows = jsonObject:GetInteger("top") NO-ERROR. + iSkipRows = jsonObject:GetInteger("skip") NO-ERROR. + ablFilter = jsonObject:GetCharacter("ablFilter") NO-ERROR. + id = jsonObject:GetCharacter("id") NO-ERROR. + cOrderBy = jsonObject:GetCharacter("orderBy") NO-ERROR. + cWhere = "WHERE " + ablFilter NO-ERROR. + + IF cOrderBy > "" THEN + DO: + cOrderBy = REPLACE(cOrderBy, ",", " by "). + cOrderBy = "by " + cOrderBy + " ". + /* NOTE: id and seq fields should be removed from + cWhere and cOrderBy */ + cOrderBy = REPLACE(cOrderBy, "by id desc", ""). + cOrderBy = REPLACE(cOrderBy, "by id ", ""). + cOrderBy = REPLACE(cOrderBy, "by seq desc", ""). + cOrderBy = REPLACE(cOrderBy, "by seq ", ""). + END. + + lUseReposition = iSkipRows <> ?. + + IF iMaxRows <> ? AND iMaxRows > 0 THEN + DO: + BUFFER ttCustomer:HANDLE:BATCH-SIZE = iMaxRows. + END. + ELSE + DO: + IF id > "" THEN + BUFFER ttCustomer:HANDLE:BATCH-SIZE = 1. + ELSE + BUFFER ttCustomer:HANDLE:BATCH-SIZE = 0. + END. + + BUFFER ttCustomer:ATTACH-DATA-SOURCE(DATA-SOURCE srcCustomer:HANDLE). + + IF cOrderBy = ? THEN cOrderBy = "". + cWhere = IF cWhere > "" THEN (cWhere + " " + cOrderBy) + ELSE ("WHERE " + cOrderBy). + DATA-SOURCE srcCustomer:FILL-WHERE-STRING = cWhere. + + IF lUseReposition THEN + DO: + hQuery = DATA-SOURCE srcCustomer:QUERY. + hQuery:QUERY-OPEN. + + IF id > "" AND id <> "?" THEN + DO: + hQuery:REPOSITION-TO-ROWID(TO-ROWID(id)). + END. + ELSE IF iSkipRows <> ? AND iSkipRows > 0 THEN + DO: + hQuery:REPOSITION-TO-ROW(iSkipRows). + IF NOT AVAILABLE Customer THEN + hQuery:GET-NEXT() NO-ERROR. + END. + + iCount = 0. + REPEAT WHILE NOT hQuery:QUERY-OFF-END AND iCount < iMaxRows: + hQuery:GET-NEXT () NO-ERROR. + IF AVAILABLE Customer THEN + DO: + CREATE ttCustomer. + BUFFER-COPY Customer TO ttCustomer. + ASSIGN + ttCustomer.id = STRING(ROWID(Customer)) + iSeq = iSeq + 1 + ttCustomer.seq = iSeq. + END. + iCount = iCount + 1. + END. + END. + ELSE + DO: + IF id > "" THEN DATA-SOURCE srcCustomer:RESTART-ROWID(1) + = TO-ROWID ((id)). + BUFFER ttCustomer:SET-CALLBACK ("AFTER-ROW-FILL", "AddIdField"). + DATASET dsCustomer:FILL(). + END. + + FINALLY: + BUFFER ttCustomer:DETACH-DATA-SOURCE(). + END FINALLY. + + END METHOD. + + METHOD PUBLIC VOID AddIdField (INPUT DATASET dsCustomer): + ASSIGN + ttCustomer.id = STRING(ROWID(Customer)) + iSeq = iSeq + 1 + ttCustomer.seq = iSeq. + END. + + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="false"). + @progress.service.resourceMapping(type="REST", operation="invoke", URI="/count?filter=~{filter~}", alias="", mediaType="application/json"). + METHOD PUBLIC VOID count( INPUT filter AS CHARACTER, OUTPUT numRecs AS INTEGER): + DEFINE VARIABLE jsonParser AS ObjectModelParser NO-UNDO. + DEFINE VARIABLE jsonObject AS JsonObject NO-UNDO. + DEFINE VARIABLE ablFilter AS CHARACTER NO-UNDO. + DEFINE VARIABLE cWhere AS CHARACTER NO-UNDO. + DEFINE VARIABLE qh AS HANDLE NO-UNDO. + + IF filter BEGINS "WHERE " THEN + cWhere = filter. + ELSE IF filter BEGINS "~{" THEN + DO: + jsonParser = NEW ObjectModelParser(). + jsonObject = CAST(jsonParser:Parse(filter), jsonObject). + ablFilter = jsonObject:GetCharacter("ablFilter") NO-ERROR. + cWhere = "WHERE " + ablFilter. + END. + ELSE IF filter NE "" THEN + DO: + /* Use filter as WHERE clause */ + cWhere = "WHERE " + filter. + END. + + CREATE QUERY qh. + qh:SET-BUFFERS(BUFFER Customer:HANDLE). + qh:QUERY-PREPARE("PRESELECT EACH Customer " + cWhere). + qh:QUERY-OPEN (). + numRecs = qh:NUM-RESULTS. + + END METHOD. + + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitSCustomer", alias="", mediaType="application/json"). + METHOD PUBLIC VOID SubmitCustomer(INPUT-OUTPUT DATASET dsCustomer): + SUPER:Submit(DATASET dsCustomer BY-REFERENCE). + FOR EACH ttCustomer: + FIND FIRST customer WHERE customer.custnum = ttCustomer.custnum NO-LOCK NO-ERROR. + IF AVAILABLE customer THEN + DO: + ttCustomer.id = STRING(ROWID(Customer)). + END. + END. + END METHOD. + +END CLASS. \ No newline at end of file diff --git a/Sports/AppServer/GetCustOrders.p b/Sports/AppServer/GetCustOrders.p new file mode 100644 index 0000000..1631159 --- /dev/null +++ b/Sports/AppServer/GetCustOrders.p @@ -0,0 +1,54 @@ + + +/** GetCustOrders.p **/ + +{"myCustOrder.i"} + + +DEFINE INPUT PARAMETER whereStr AS CHAR. +DEFINE OUTPUT PARAMETER DATASET-HANDLE Hdl. + + +DEFINE VAR prepareExp AS CHAR NO-UNDO. +DEFINE VAR orderCnt AS INT INIT 0. + +DEF VAR retok AS LOG. +DEF VAR hCustOrders AS HANDLE. +DEF VAR hqCust AS HANDLE. +DEF VAR sError AS CHAR. + +DEFINE QUERY qCust FOR Customer. +hqCust = QUERY qCust:HANDLE. + +prepareExp = "FOR EACH Customer". + +IF whereStr BEGINS "WHERE " THEN + prepareExp = prepareExp + " " + whereStr. +ELSE IF whereStr NE "" THEN + prepareExp = prepareExp + " WHERE " + whereStr. + +MESSAGE "GetCustOrders.p: prepareExp is: " prepareExp. + +retok = hqCust:QUERY-PREPARE(prepareExp). + + +IF NOT retok THEN +DO: + // sError = "QUERY-PREPARE failed" . + RETURN. +END. + +DEFINE DATA-SOURCE dsCust FOR QUERY qCust. +DEFINE DATA-SOURCE dsOrder FOR Order. + +DATASET dsCustomerOrder:EMPTY-DATASET(). + +BUFFER ttCustomer:HANDLE:ATTACH-DATA-SOURCE(DATA-SOURCE dsCust:HANDLE,?,?,?). + +BUFFER ttOrder:HANDLE:ATTACH-DATA-SOURCE(DATA-SOURCE dsOrder:HANDLE,?,?,?). + +retok = DATASET dsCustomerOrder:FILL(). + +Hdl = DATASET dsCustomerOrder:HANDLE. + + diff --git a/Sports/AppServer/Item.cls b/Sports/AppServer/Item.cls new file mode 100644 index 0000000..acf1256 --- /dev/null +++ b/Sports/AppServer/Item.cls @@ -0,0 +1,118 @@ + + /*------------------------------------------------------------------------ + File : Item + Syntax : + Author(s) : anikumar + Created : Fri Jul 27 14:16:02 EDT 2018 + Notes : + ----------------------------------------------------------------------*/ + +@program FILE(name="Item.cls", module="AppServer"). +@openapi.openedge.export FILE(type="REST", executionMode="singleton", useReturnValue="false", writeDataSetBeforeImage="false"). +@progress.service.resource FILE(name="Item", URI="/Item", schemaName="dsItem", schemaFile="Sports/AppServer/item.i"). + +USING Progress.Lang.*. +USING OpenEdge.BusinessLogic.BusinessEntity. + +BLOCK-LEVEL ON ERROR UNDO, THROW. + +CLASS Item INHERITS BusinessEntity: + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + {"item.i"} + + DEFINE DATA-SOURCE srcItem FOR sports.Item. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + CONSTRUCTOR PUBLIC Item(): + + DEFINE VARIABLE hDataSourceArray AS HANDLE NO-UNDO EXTENT 1. + DEFINE VARIABLE cSkipListArray AS CHARACTER NO-UNDO EXTENT 1. + + SUPER (DATASET dsItem:HANDLE). + + /* Data Source for each table in dataset. Should be in table order as defined + in DataSet */ + hDataSourceArray[1] = DATA-SOURCE srcItem:HANDLE. + + + /* Skip-list entry for each table in dataset. Should be in temp-table order + as defined in DataSet */ + /* Each skip-list entry is a comma-separated list of field names, to be + ignored in create stmt */ + + cSkipListArray[1] = "". + + + THIS-OBJECT:ProDataSource = hDataSourceArray. + THIS-OBJECT:SkipList = cSkipListArray. + + END CONSTRUCTOR. + + /*------------------------------------------------------------------------------ + Purpose: Get one or more records, based on a filter string + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="read", URI="?filter=~{filter~}", alias="", mediaType="application/json"). + METHOD PUBLIC VOID ReadItem( + INPUT filter AS CHARACTER, + OUTPUT DATASET dsItem): + + SUPER:ReadData(filter). + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Create one or more new records + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="create", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID CreateItem(INPUT-OUTPUT DATASET dsItem): + + SUPER:CreateData(DATASET dsItem BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Update one or more records + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="update", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID UpdateItem(INPUT-OUTPUT DATASET dsItem): + + SUPER:UpdateData(DATASET dsItem BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Delete a record + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="delete", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID DeleteItem(INPUT-OUTPUT DATASET dsItem): + + SUPER:DeleteData(DATASET dsItem BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Submit a record + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitItem", alias="", mediaType="application/json"). + METHOD PUBLIC VOID SubmitItem(INPUT-OUTPUT DATASET dsItem): + + SUPER:Submit(DATASET dsItem BY-REFERENCE). + END METHOD. + + +END CLASS. diff --git a/Sports/AppServer/Order.cls b/Sports/AppServer/Order.cls new file mode 100644 index 0000000..8e111b5 --- /dev/null +++ b/Sports/AppServer/Order.cls @@ -0,0 +1,118 @@ + + /*------------------------------------------------------------------------ + File : Order + Syntax : + Author(s) : anikumar + Created : Fri Jul 27 14:16:21 EDT 2018 + Notes : + ----------------------------------------------------------------------*/ + +@program FILE(name="Order.cls", module="AppServer"). +@openapi.openedge.export FILE(type="REST", executionMode="singleton", useReturnValue="false", writeDataSetBeforeImage="false"). +@progress.service.resource FILE(name="Order", URI="/Order", schemaName="dsOrder", schemaFile="Sports/AppServer/order.i"). + +USING Progress.Lang.*. +USING OpenEdge.BusinessLogic.BusinessEntity. + +BLOCK-LEVEL ON ERROR UNDO, THROW. + +CLASS Order INHERITS BusinessEntity: + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + {"order.i"} + + DEFINE DATA-SOURCE srcOrder FOR sports.Order. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + CONSTRUCTOR PUBLIC Order(): + + DEFINE VARIABLE hDataSourceArray AS HANDLE NO-UNDO EXTENT 1. + DEFINE VARIABLE cSkipListArray AS CHARACTER NO-UNDO EXTENT 1. + + SUPER (DATASET dsOrder:HANDLE). + + /* Data Source for each table in dataset. Should be in table order as defined + in DataSet */ + hDataSourceArray[1] = DATA-SOURCE srcOrder:HANDLE. + + + /* Skip-list entry for each table in dataset. Should be in temp-table order + as defined in DataSet */ + /* Each skip-list entry is a comma-separated list of field names, to be + ignored in create stmt */ + + cSkipListArray[1] = "". + + + THIS-OBJECT:ProDataSource = hDataSourceArray. + THIS-OBJECT:SkipList = cSkipListArray. + + END CONSTRUCTOR. + + /*------------------------------------------------------------------------------ + Purpose: Get one or more records, based on a filter string + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="read", URI="?filter=~{filter~}", alias="", mediaType="application/json"). + METHOD PUBLIC VOID ReadOrder( + INPUT filter AS CHARACTER, + OUTPUT DATASET dsOrder): + + SUPER:ReadData(filter). + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Create one or more new records + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="create", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID CreateOrder(INPUT-OUTPUT DATASET dsOrder): + + SUPER:CreateData(DATASET dsOrder BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Update one or more records + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="update", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID UpdateOrder(INPUT-OUTPUT DATASET dsOrder): + + SUPER:UpdateData(DATASET dsOrder BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Delete a record + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="delete", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID DeleteOrder(INPUT-OUTPUT DATASET dsOrder): + + SUPER:DeleteData(DATASET dsOrder BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Submit a record + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitOrder", alias="", mediaType="application/json"). + METHOD PUBLIC VOID SubmitOrder(INPUT-OUTPUT DATASET dsOrder): + + SUPER:Submit(DATASET dsOrder BY-REFERENCE). + END METHOD. + + +END CLASS. diff --git a/Sports/AppServer/Salesrep.cls b/Sports/AppServer/Salesrep.cls new file mode 100644 index 0000000..3e8557e --- /dev/null +++ b/Sports/AppServer/Salesrep.cls @@ -0,0 +1,118 @@ + + /*------------------------------------------------------------------------ + File : Salesrep + Syntax : + Author(s) : anikumar + Created : Fri Jul 27 14:15:22 EDT 2018 + Notes : + ----------------------------------------------------------------------*/ + +@program FILE(name="Salesrep.cls", module="AppServer"). +@openapi.openedge.export FILE(type="REST", executionMode="singleton", useReturnValue="false", writeDataSetBeforeImage="false"). +@progress.service.resource FILE(name="Salesrep", URI="/Salesrep", schemaName="dsSalesrep", schemaFile="Sports/AppServer/salesrep.i"). + +USING Progress.Lang.*. +USING OpenEdge.BusinessLogic.BusinessEntity. + +BLOCK-LEVEL ON ERROR UNDO, THROW. + +CLASS Salesrep INHERITS BusinessEntity: + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + {"salesrep.i"} + + DEFINE DATA-SOURCE srcSalesrep FOR sports.Salesrep. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + CONSTRUCTOR PUBLIC Salesrep(): + + DEFINE VARIABLE hDataSourceArray AS HANDLE NO-UNDO EXTENT 1. + DEFINE VARIABLE cSkipListArray AS CHARACTER NO-UNDO EXTENT 1. + + SUPER (DATASET dsSalesrep:HANDLE). + + /* Data Source for each table in dataset. Should be in table order as defined + in DataSet */ + hDataSourceArray[1] = DATA-SOURCE srcSalesrep:HANDLE. + + + /* Skip-list entry for each table in dataset. Should be in temp-table order + as defined in DataSet */ + /* Each skip-list entry is a comma-separated list of field names, to be + ignored in create stmt */ + + cSkipListArray[1] = "". + + + THIS-OBJECT:ProDataSource = hDataSourceArray. + THIS-OBJECT:SkipList = cSkipListArray. + + END CONSTRUCTOR. + + /*------------------------------------------------------------------------------ + Purpose: Get one or more records, based on a filter string + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="read", URI="?filter=~{filter~}", alias="", mediaType="application/json"). + METHOD PUBLIC VOID ReadSalesrep( + INPUT filter AS CHARACTER, + OUTPUT DATASET dsSalesrep): + + SUPER:ReadData(filter). + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Create one or more new records + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="create", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID CreateSalesrep(INPUT-OUTPUT DATASET dsSalesrep): + + SUPER:CreateData(DATASET dsSalesrep BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Update one or more records + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="update", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID UpdateSalesrep(INPUT-OUTPUT DATASET dsSalesrep): + + SUPER:UpdateData(DATASET dsSalesrep BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Delete a record + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="delete", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID DeleteSalesrep(INPUT-OUTPUT DATASET dsSalesrep): + + SUPER:DeleteData(DATASET dsSalesrep BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Submit a record + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitSalesrep", alias="", mediaType="application/json"). + METHOD PUBLIC VOID SubmitSalesrep(INPUT-OUTPUT DATASET dsSalesrep): + + SUPER:Submit(DATASET dsSalesrep BY-REFERENCE). + END METHOD. + + +END CLASS. diff --git a/Sports/AppServer/State.cls b/Sports/AppServer/State.cls new file mode 100644 index 0000000..23cf237 --- /dev/null +++ b/Sports/AppServer/State.cls @@ -0,0 +1,118 @@ + + /*------------------------------------------------------------------------ + File : State + Syntax : + Author(s) : anikumar + Created : Fri Jul 27 14:14:58 EDT 2018 + Notes : + ----------------------------------------------------------------------*/ + +@program FILE(name="State.cls", module="AppServer"). +@openapi.openedge.export FILE(type="REST", executionMode="singleton", useReturnValue="false", writeDataSetBeforeImage="false"). +@progress.service.resource FILE(name="State", URI="/State", schemaName="dsState", schemaFile="Sports/AppServer/state.i"). + +USING Progress.Lang.*. +USING OpenEdge.BusinessLogic.BusinessEntity. + +BLOCK-LEVEL ON ERROR UNDO, THROW. + +CLASS State INHERITS BusinessEntity: + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + {"state.i"} + + DEFINE DATA-SOURCE srcState FOR sports.State. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + CONSTRUCTOR PUBLIC State(): + + DEFINE VARIABLE hDataSourceArray AS HANDLE NO-UNDO EXTENT 1. + DEFINE VARIABLE cSkipListArray AS CHARACTER NO-UNDO EXTENT 1. + + SUPER (DATASET dsState:HANDLE). + + /* Data Source for each table in dataset. Should be in table order as defined + in DataSet */ + hDataSourceArray[1] = DATA-SOURCE srcState:HANDLE. + + + /* Skip-list entry for each table in dataset. Should be in temp-table order + as defined in DataSet */ + /* Each skip-list entry is a comma-separated list of field names, to be + ignored in create stmt */ + + cSkipListArray[1] = "". + + + THIS-OBJECT:ProDataSource = hDataSourceArray. + THIS-OBJECT:SkipList = cSkipListArray. + + END CONSTRUCTOR. + + /*------------------------------------------------------------------------------ + Purpose: Get one or more records, based on a filter string + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="read", URI="?filter=~{filter~}", alias="", mediaType="application/json"). + METHOD PUBLIC VOID ReadState( + INPUT filter AS CHARACTER, + OUTPUT DATASET dsState): + + SUPER:ReadData(filter). + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Create one or more new records + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="create", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID CreateState(INPUT-OUTPUT DATASET dsState): + + SUPER:CreateData(DATASET dsState BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Update one or more records + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="update", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID UpdateState(INPUT-OUTPUT DATASET dsState): + + SUPER:UpdateData(DATASET dsState BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Delete a record + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="delete", URI="", alias="", mediaType="application/json"). + METHOD PUBLIC VOID DeleteState(INPUT-OUTPUT DATASET dsState): + + SUPER:DeleteData(DATASET dsState BY-REFERENCE). + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: Submit a record + Notes: + ------------------------------------------------------------------------------*/ + @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). + @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitState", alias="", mediaType="application/json"). + METHOD PUBLIC VOID SubmitState(INPUT-OUTPUT DATASET dsState): + + SUPER:Submit(DATASET dsState BY-REFERENCE). + END METHOD. + + +END CLASS. diff --git a/Sports/AppServer/UpdateCustOrders.p b/Sports/AppServer/UpdateCustOrders.p new file mode 100644 index 0000000..a353ce3 --- /dev/null +++ b/Sports/AppServer/UpdateCustOrders.p @@ -0,0 +1,53 @@ + + + + +/** UpdateCustOrders.p **/ + + {"myCustOrder.i"} + + +FUNCTION GetErrorMsg RETURNS CHAR: + DEFINE VAR errMsg AS CHAR. + + IF ERROR-STATUS:NUM-MESSAGES >= 1 THEN + errMsg = ERROR-STATUS:GET-MESSAGE(1). + ELSE + errMsg = RETURN-VALUE. + + IF errMsg EQ ? THEN + errMsg = "Error occurred". + + RETURN errMsg. +END FUNCTION. + + +DEFINE INPUT-OUTPUT PARAMETER DATASET FOR dsCustomerOrder. +DEFINE OUTPUT PARAMETER statusMsg AS CHAR INIT ?. + + DEFINE DATA-SOURCE dsCust FOR Customer. + DEFINE DATA-SOURCE dsOrder FOR Order. + + BUFFER ttCustomer:ATTACH-DATA-SOURCE (DATA-SOURCE dsCust:HANDLE). + BUFFER ttOrder:ATTACH-DATA-SOURCE (DATA-SOURCE dsOrder:HANDLE). + + FOR EACH bttCustomer: + MESSAGE "UpdateCustOrders.p, UPDATING CustNum: " bttCustomer.Custnum. + BUFFER bttCustomer:SAVE-ROW-CHANGES(1, "CustNum") NO-ERROR. + IF BUFFER bttCustomer:ERROR THEN + statusMsg = GetErrorMsg(). + END. + + FOR EACH bttOrder: + BUFFER bttOrder:SAVE-ROW-CHANGES(1, "Ordernum") NO-ERROR. + IF BUFFER bttOrder:ERROR THEN + statusMsg = GetErrorMsg(). + END. + + BUFFER bttCustomer:DETACH-DATA-SOURCE(). + BUFFER bttOrder:DETACH-DATA-SOURCE(). + + IF statusMsg EQ ? THEN + statusMsg = "Okay". + + diff --git a/Sports/AppServer/customer.i b/Sports/AppServer/customer.i new file mode 100644 index 0000000..320d00d --- /dev/null +++ b/Sports/AppServer/customer.i @@ -0,0 +1,27 @@ +@openapi.openedge.entity.property (name="idProperty", value="id"). +DEFINE TEMP-TABLE ttCustomer BEFORE-TABLE bttCustomer + FIELD id AS CHARACTER + FIELD seq AS INTEGER INITIAL ? + FIELD CustNum AS INTEGER INITIAL "0" LABEL "Cust Num" + FIELD Name AS CHARACTER LABEL "Name" + FIELD Address AS CHARACTER LABEL "Address" + FIELD Address2 AS CHARACTER LABEL "Address2" + FIELD Balance AS DECIMAL INITIAL "0" LABEL "Balance" + FIELD City AS CHARACTER LABEL "City" + FIELD Comments AS CHARACTER LABEL "Comments" + FIELD Contact AS CHARACTER LABEL "Contact" + FIELD Country AS CHARACTER INITIAL "USA" LABEL "Country" + FIELD CreditLimit AS DECIMAL INITIAL "1500" LABEL "Credit Limit" + FIELD Discount AS INTEGER INITIAL "0" LABEL "Discount" + FIELD EmailAddress AS CHARACTER LABEL "Email" + FIELD Fax AS CHARACTER LABEL "Fax" + FIELD Phone AS CHARACTER LABEL "Phone" + FIELD PostalCode AS CHARACTER LABEL "Postal Code" + FIELD SalesRep AS CHARACTER LABEL "Sales Rep" + FIELD State AS CHARACTER LABEL "State" + FIELD Terms AS CHARACTER INITIAL "Net30" LABEL "Terms" + INDEX seq IS PRIMARY UNIQUE seq + /* INDEX CustNum IS UNIQUE CustNum */ + . + +DEFINE DATASET dsCustomer FOR ttCustomer. \ No newline at end of file diff --git a/Sports/AppServer/item.i b/Sports/AppServer/item.i new file mode 100644 index 0000000..acf0ce7 --- /dev/null +++ b/Sports/AppServer/item.i @@ -0,0 +1,44 @@ + + /*------------------------------------------------------------------------ + File : Item + Purpose : + Syntax : + Description : + Author(s) : Administrator + Created : Fri Jul 27 14:16:02 EDT 2018 + Notes : + ----------------------------------------------------------------------*/ + + /* *************************** Definitions ************************** */ + + /* ******************** Preprocessor Definitions ******************** */ + + /* *************************** Main Block *************************** */ + + /** Dynamically generated schema file **/ + +@openapi.openedge.entity.primarykey (fields="Itemnum"). + +DEFINE TEMP-TABLE ttItem BEFORE-TABLE bttItem +FIELD Itemnum AS INTEGER INITIAL "0" LABEL "Item Num" +FIELD ItemName AS CHARACTER LABEL "Item Name" +FIELD Price AS DECIMAL INITIAL "0" LABEL "Price" +FIELD Onhand AS INTEGER INITIAL "0" LABEL "On Hand" +FIELD Allocated AS INTEGER INITIAL "0" LABEL "Allocated" +FIELD ReOrder AS INTEGER INITIAL "0" LABEL "Re Order" +FIELD OnOrder AS INTEGER INITIAL "0" LABEL "On Order" +FIELD CatPage AS INTEGER INITIAL "0" LABEL "Cat Page" +FIELD CatDescription AS CHARACTER LABEL "Cat-Description" +FIELD Category1 AS CHARACTER LABEL "Category1" +FIELD Category2 AS CHARACTER LABEL "Category2" +FIELD Special AS CHARACTER LABEL "Special" +FIELD Weight AS DECIMAL INITIAL "0" LABEL "Weight" +FIELD Minqty AS INTEGER INITIAL "0" LABEL "Min Qty" +INDEX CatDescription CatDescription ASCENDING +INDEX Category2ItemName Category2 ASCENDING ItemName ASCENDING +INDEX CategoryItemName Category1 ASCENDING ItemName ASCENDING +INDEX ItemName ItemName ASCENDING +INDEX ItemNum IS PRIMARY UNIQUE Itemnum ASCENDING . + + +DEFINE DATASET dsItem FOR ttItem. \ No newline at end of file diff --git a/Sports/AppServer/myCustOrder.i b/Sports/AppServer/myCustOrder.i new file mode 100644 index 0000000..91b1cdf --- /dev/null +++ b/Sports/AppServer/myCustOrder.i @@ -0,0 +1,34 @@ + +DEFINE TEMP-TABLE ttCustomer BEFORE-TABLE bttCustomer + FIELD CustNum AS INTEGER INITIAL "0" LABEL "Cust Num" + FIELD Name AS CHARACTER LABEL "Name" + FIELD Address AS CHARACTER LABEL "Address" + FIELD Address2 AS CHARACTER LABEL "Address2" + FIELD Balance AS DECIMAL INITIAL "0" LABEL "Balance" + FIELD City AS CHARACTER LABEL "City" + FIELD Contact AS CHARACTER LABEL "Contact" + FIELD Country AS CHARACTER INITIAL "USA" LABEL "Country" + FIELD CreditLimit AS DECIMAL INITIAL "1500" LABEL "Credit Limit" + FIELD Discount AS INTEGER INITIAL "0" LABEL "Discount" + FIELD EmailAddress AS CHARACTER LABEL "Email" + FIELD Phone AS CHARACTER LABEL "Phone" + + INDEX CustNum IS UNIQUE CustNum. + + +DEFINE TEMP-TABLE ttOrder BEFORE-TABLE bttOrder + FIELD Ordernum AS INTEGER + FIELD CustNum AS INTEGER + FIELD OrderDate AS DATE + FIELD SalesRep AS CHARACTER + FIELD OrderStatus AS CHARACTER INITIAL "Ordered" + + INDEX CustOrderIdx IS UNIQUE CustNum Ordernum + INDEX OrdernumIdx IS UNIQUE PRIMARY Ordernum. + + +DEFINE DATASET dsCustomerOrder + FOR ttCustomer, ttOrder + DATA-RELATION custOrdRel FOR ttCustomer, ttOrder + RELATION-FIELDS (CustNum, CustNum). + diff --git a/Sports/AppServer/order.i b/Sports/AppServer/order.i new file mode 100644 index 0000000..f36375b --- /dev/null +++ b/Sports/AppServer/order.i @@ -0,0 +1,45 @@ + + /*------------------------------------------------------------------------ + File : Order + Purpose : + Syntax : + Description : + Author(s) : Administrator + Created : Fri Jul 27 14:16:21 EDT 2018 + Notes : + ----------------------------------------------------------------------*/ + + /* *************************** Definitions ************************** */ + + /* ******************** Preprocessor Definitions ******************** */ + + /* *************************** Main Block *************************** */ + + /** Dynamically generated schema file **/ + +@openapi.openedge.entity.primarykey (fields="Ordernum"). + +DEFINE TEMP-TABLE ttOrder BEFORE-TABLE bttOrder +FIELD Ordernum AS INTEGER INITIAL "0" LABEL "Order Num" +FIELD CustNum AS INTEGER INITIAL "0" LABEL "Cust Num" +FIELD OrderDate AS DATE INITIAL "TODAY" LABEL "Ordered" +FIELD ShipDate AS DATE LABEL "Shipped" +FIELD PromiseDate AS DATE LABEL "Promised" +FIELD Carrier AS CHARACTER LABEL "Carrier" +FIELD Instructions AS CHARACTER LABEL "Instructions" +FIELD PO AS CHARACTER LABEL "PO" +FIELD Terms AS CHARACTER INITIAL "Net30" LABEL "Terms" +FIELD SalesRep AS CHARACTER LABEL "Sales Rep" +FIELD BillToID AS INTEGER INITIAL "0" LABEL "Bill To ID" +FIELD ShipToID AS INTEGER INITIAL "0" LABEL "Ship To ID" +FIELD OrderStatus AS CHARACTER INITIAL "Ordered" LABEL "Order Status" +FIELD WarehouseNum AS INTEGER INITIAL "0" LABEL "Warehouse Num" +FIELD Creditcard AS CHARACTER INITIAL "Visa" LABEL "Credit Card" +INDEX CustOrder IS UNIQUE CustNum ASCENDING Ordernum ASCENDING +INDEX OrderDate OrderDate ASCENDING +INDEX OrderNum IS PRIMARY UNIQUE Ordernum ASCENDING +INDEX OrderStatus OrderStatus ASCENDING +INDEX SalesRep SalesRep ASCENDING . + + +DEFINE DATASET dsOrder FOR ttOrder. \ No newline at end of file diff --git a/Sports/AppServer/salesrep.i b/Sports/AppServer/salesrep.i new file mode 100644 index 0000000..e4e301b --- /dev/null +++ b/Sports/AppServer/salesrep.i @@ -0,0 +1,30 @@ + + /*------------------------------------------------------------------------ + File : Salesrep + Purpose : + Syntax : + Description : + Author(s) : Administrator + Created : Fri Jul 27 14:15:22 EDT 2018 + Notes : + ----------------------------------------------------------------------*/ + + /* *************************** Definitions ************************** */ + + /* ******************** Preprocessor Definitions ******************** */ + + /* *************************** Main Block *************************** */ + + /** Dynamically generated schema file **/ + +@openapi.openedge.entity.primarykey (fields="SalesRep"). + +DEFINE TEMP-TABLE ttSalesrep BEFORE-TABLE bttSalesrep +FIELD SalesRep AS CHARACTER LABEL "Sales Rep" +FIELD RepName AS CHARACTER LABEL "Rep Name" +FIELD Region AS CHARACTER LABEL "Region" +FIELD MonthQuota AS INTEGER EXTENT 12 INITIAL "0" LABEL "Month Quota" +INDEX SalesRep IS PRIMARY UNIQUE SalesRep ASCENDING . + + +DEFINE DATASET dsSalesrep FOR ttSalesrep. \ No newline at end of file diff --git a/Sports/AppServer/sports2020trgs/asordnum.p b/Sports/AppServer/sports2020trgs/asordnum.p new file mode 100644 index 0000000..2f5c212 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/asordnum.p @@ -0,0 +1,12 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: asordnum.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR ASSIGN OF OrderLine.Ordernum. + +MESSAGE "Order Number Assigned". diff --git a/Sports/AppServer/sports2020trgs/asstate.p b/Sports/AppServer/sports2020trgs/asstate.p new file mode 100644 index 0000000..d8947d6 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/asstate.p @@ -0,0 +1,31 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: asstate.p +** Descript: To test, enter an Invalid Two-Character State Abbrev. +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Assign OF Customer.State. + +IF Customer.Country = "USA" AND NOT (CAN-FIND(State OF Customer)) +THEN DO: + MESSAGE "Illegal State name for the U.S.A." + VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. + RETURN ERROR. +END. + + + + + + + + + + + + + + diff --git a/Sports/AppServer/sports2020trgs/crbin.p b/Sports/AppServer/sports2020trgs/crbin.p new file mode 100644 index 0000000..0b4399a --- /dev/null +++ b/Sports/AppServer/sports2020trgs/crbin.p @@ -0,0 +1,15 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: crbin.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Create OF Bin. + +/* Automatically Increment Customer Number using NextCustNum Sequence */ + +ASSIGN Bin.BinNum = NEXT-VALUE(NextBinNum). + diff --git a/Sports/AppServer/sports2020trgs/crcust.p b/Sports/AppServer/sports2020trgs/crcust.p new file mode 100644 index 0000000..7637d5e --- /dev/null +++ b/Sports/AppServer/sports2020trgs/crcust.p @@ -0,0 +1,15 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: crcust.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Create OF Customer. + +/* Automatically Increment Customer Number using NextCustNum Sequence */ + +ASSIGN Customer.CustNum = NEXT-VALUE(NextCustNum). + diff --git a/Sports/AppServer/sports2020trgs/cremp.p b/Sports/AppServer/sports2020trgs/cremp.p new file mode 100644 index 0000000..4167953 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/cremp.p @@ -0,0 +1,4 @@ +TRIGGER PROCEDURE FOR CREATE OF Employee. + +ASSIGN Employee.EmpNum = NEXT-VALUE(NextEmpNum). + diff --git a/Sports/AppServer/sports2020trgs/crintr.p b/Sports/AppServer/sports2020trgs/crintr.p new file mode 100644 index 0000000..7024793 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/crintr.p @@ -0,0 +1,4 @@ +TRIGGER PROCEDURE FOR CREATE OF InventoryTrans. + +ASSIGN invtransnum = NEXT-VALUE(NextInvTransNum). + diff --git a/Sports/AppServer/sports2020trgs/crinv.p b/Sports/AppServer/sports2020trgs/crinv.p new file mode 100644 index 0000000..3c4aead --- /dev/null +++ b/Sports/AppServer/sports2020trgs/crinv.p @@ -0,0 +1,14 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: crinv.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Create OF Invoice. + +/* Automatically increment Invoice Number using Next-Inv-Num Sequence */ + +ASSIGN invoice.invoicenum = NEXT-VALUE(NEXTINVNUM). diff --git a/Sports/AppServer/sports2020trgs/critem.p b/Sports/AppServer/sports2020trgs/critem.p new file mode 100644 index 0000000..9381318 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/critem.p @@ -0,0 +1,14 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: critem.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Create OF Item. + +/* Automatically assign a unique item number using NextItemNum Sequence */ + +ASSIGN Item.ItemNum = NEXT-VALUE(NextItemNum). diff --git a/Sports/AppServer/sports2020trgs/crlocdef.p b/Sports/AppServer/sports2020trgs/crlocdef.p new file mode 100644 index 0000000..39deb61 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/crlocdef.p @@ -0,0 +1,4 @@ +TRIGGER PROCEDURE FOR CREATE OF LocalDefault. + +ASSIGN LocalDefault.LocalDefNum = NEXT-VALUE(NextLocalDefNum). + diff --git a/Sports/AppServer/sports2020trgs/crord.p b/Sports/AppServer/sports2020trgs/crord.p new file mode 100644 index 0000000..f758e21 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/crord.p @@ -0,0 +1,19 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: crord.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Create OF Order. + +/* Automatically Increment Order-Number using Next-Ord-Num Sequence */ + +ASSIGN order.ordernum = NEXT-VALUE(NextOrdNum) + +/* Set Order Date to TODAY, Promise Date to 2 weeks from TODAY */ + +order.orderdate = TODAY +order.promisedate = TODAY + 14. diff --git a/Sports/AppServer/sports2020trgs/crordl.p b/Sports/AppServer/sports2020trgs/crordl.p new file mode 100644 index 0000000..7483c16 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/crordl.p @@ -0,0 +1,11 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: crordl.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Create OF OrderLine. + diff --git a/Sports/AppServer/sports2020trgs/crpo.p b/Sports/AppServer/sports2020trgs/crpo.p new file mode 100644 index 0000000..28f341d --- /dev/null +++ b/Sports/AppServer/sports2020trgs/crpo.p @@ -0,0 +1,4 @@ +TRIGGER PROCEDURE FOR CREATE OF PurchaseOrder. + +ASSIGN PurchaseOrder.PONum = NEXT-VALUE(NextPONum). + diff --git a/Sports/AppServer/sports2020trgs/crsuppl.p b/Sports/AppServer/sports2020trgs/crsuppl.p new file mode 100644 index 0000000..204b800 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/crsuppl.p @@ -0,0 +1,4 @@ +TRIGGER PROCEDURE FOR CREATE OF Supplier. + +ASSIGN Supplier.SupplierIDNum = NEXT-VALUE(NextSupplNum). + diff --git a/Sports/AppServer/sports2020trgs/crware.p b/Sports/AppServer/sports2020trgs/crware.p new file mode 100644 index 0000000..1016d99 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/crware.p @@ -0,0 +1,3 @@ +TRIGGER PROCEDURE FOR CREATE OF Warehouse. + +ASSIGN Warehouse.WarehouseNum = NEXT-VALUE(NextWareNum). diff --git a/Sports/AppServer/sports2020trgs/delcust.p b/Sports/AppServer/sports2020trgs/delcust.p new file mode 100644 index 0000000..a835cdf --- /dev/null +++ b/Sports/AppServer/sports2020trgs/delcust.p @@ -0,0 +1,51 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: delcust.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER Procedure FOR Delete OF Customer. + +/* Variable Definitions */ + +DEFINE VARIABLE answer AS LOGICAL. + +/* Customer record cannot be deleted if outstanding invoices are found */ + +FIND FIRST invoice OF customer NO-ERROR. +IF AVAILABLE invoice THEN DO: + IF invoice.amount <= invoice.totalpaid + invoice.adjustment THEN DO: + MESSAGE "Invoice OK, looking for Orders..." + VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. + FIND FIRST order OF customer NO-ERROR. + IF NOT AVAILABLE order THEN DO: + RETURN. + END. + ELSE DO: + MESSAGE "Open orders exist for Customer " customer.custnum + ". Cannot delete." + VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. + RETURN ERROR. + END. + END. + ELSE DO: + MESSAGE "Outstanding Unpaid Invoice Exists, Cannot Delete" + VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. + RETURN ERROR. + END. +END. +ELSE DO: + FIND FIRST order OF customer NO-ERROR. + IF NOT AVAILABLE order THEN DO: + RETURN. + END. + ELSE DO: + MESSAGE "Open orders exist for Customer " customer.custnum + ". Cannot delete." + VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. + RETURN ERROR. + END. +END. diff --git a/Sports/AppServer/sports2020trgs/delinv.p b/Sports/AppServer/sports2020trgs/delinv.p new file mode 100644 index 0000000..cf63502 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/delinv.p @@ -0,0 +1,19 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: delinv.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Delete OF Invoice. + +/* Invoices cannot be deleted if the Invoice amount exceeds Total-Paid + Adjustment */ + +IF Invoice.Amount > Invoice.TotalPaid + Invoice.Adjustment +THEN DO: + MESSAGE "The Invoice Amount cannot be greater than TotalPaid + Adjustment" + VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. + RETURN ERROR. +END. diff --git a/Sports/AppServer/sports2020trgs/delitem.p b/Sports/AppServer/sports2020trgs/delitem.p new file mode 100644 index 0000000..5b676d3 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/delitem.p @@ -0,0 +1,7 @@ +TRIGGER PROCEDURE FOR DELETE OF Item. + +FOR EACH bin OF item: + + DELETE bin. + +END. /*for each bin*/ diff --git a/Sports/AppServer/sports2020trgs/delord.p b/Sports/AppServer/sports2020trgs/delord.p new file mode 100644 index 0000000..fa221ca --- /dev/null +++ b/Sports/AppServer/sports2020trgs/delord.p @@ -0,0 +1,29 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: delord.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Delete OF Order. + +/* When Orders are deleted, associated Order detail lines (OrderLine) + * are also deleted. + */ + +MESSAGE "Deleting Order" OrderNum VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. +FOR EACH OrderLine OF Order: + DELETE OrderLine. +END. + + + + + + + + + + diff --git a/Sports/AppServer/sports2020trgs/delordl.p b/Sports/AppServer/sports2020trgs/delordl.p new file mode 100644 index 0000000..6819558 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/delordl.p @@ -0,0 +1,16 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: delord.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Delete OF OrderLine. + +/* Trigger provides an information message when orderlines are deleted */ + +MESSAGE "Deleting Order Line:" Linenum "Order Num:" OrderNum + VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. + diff --git a/Sports/AppServer/sports2020trgs/delsup.p b/Sports/AppServer/sports2020trgs/delsup.p new file mode 100644 index 0000000..24cb58b --- /dev/null +++ b/Sports/AppServer/sports2020trgs/delsup.p @@ -0,0 +1,28 @@ +TRIGGER PROCEDURE FOR DELETE OF Supplier. + +FIND FIRST purchaseorder OF supplier WHERE postatus <> "Received" +NO-LOCK NO-ERROR. + +IF AVAIL purchaseorder THEN DO: + + MESSAGE "Supplier can not be deleted." + "There is at least one PO that has not been received." + VIEW-AS ALERT-BOX ERROR BUTTONS OK. + RETURN ERROR. + +END. /*if avail purchaseorder*/ + +ELSE DO: + /*delete received po*/ + + FOR EACH purchaseorder OF Supplier: + + FOR EACH poline OF purchaseorder: + DELETE poline. + END. /*for each poline*/ + + DELETE purchaseorder. + + END. /*for each purchaseorder of supplier*/ + +END. /*else do*/ diff --git a/Sports/AppServer/sports2020trgs/ref_call.p b/Sports/AppServer/sports2020trgs/ref_call.p new file mode 100644 index 0000000..cf6549b --- /dev/null +++ b/Sports/AppServer/sports2020trgs/ref_call.p @@ -0,0 +1,13 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: ref_call.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +/* Assign trigger for the Ref-Call table */ +TRIGGER PROCEDURE FOR Create OF REFCALL. + +ASSIGN RefCall.CallNum = STRING(NEXT-VALUE(NextRefNum)). diff --git a/Sports/AppServer/sports2020trgs/wrcust.p b/Sports/AppServer/sports2020trgs/wrcust.p new file mode 100644 index 0000000..61133eb --- /dev/null +++ b/Sports/AppServer/sports2020trgs/wrcust.p @@ -0,0 +1,52 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: wrcust.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Write OF Customer OLD BUFFER oldCustomer. + +/* Variable Definitions */ + +DEFINE VARIABLE i AS INTEGER INITIAL 0. +DEFINE VARIABLE Outstanding AS INTEGER INITIAL 0. + +/* Check to see if the user changed the Customer Number */ + +IF Customer.CustNum <> oldCustomer.CustNum AND oldCustomer.CustNum <> 0 +THEN DO: + + /* If user changed the Customer Number, find related orders and change */ + /* their customer numbers. */ + + FOR EACH order OF oldCustomer: + Order.CustNum = Customer.CustNum. + i = i + 1. + END. + IF i > 0 THEN + MESSAGE i "orders changed to reflect the new customer number!" + VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. +END. + +/* Ensure that the Credit Limit value is always Greater than the sum of this + * Customer's Outstanding Balance + */ + +FOR EACH Order OF Customer: + FOR EACH OrderLine OF Order where order.shipdate EQ ?: + Outstanding = Outstanding + OrderLine.ExtendedPrice. + END. +END. +FOR EACH Invoice OF Customer: + Outstanding = Outstanding + ( Amount - ( TotalPaid + Adjustment )). +END. + +IF Customer.CreditLimit < Outstanding THEN DO: + MESSAGE "This Customer has an outstanding balance of: " Outstanding + ". The Credit Limit MUST exceed this amount!" + VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. + RETURN ERROR. +END. diff --git a/Sports/AppServer/sports2020trgs/writem.p b/Sports/AppServer/sports2020trgs/writem.p new file mode 100644 index 0000000..f9980cc --- /dev/null +++ b/Sports/AppServer/sports2020trgs/writem.p @@ -0,0 +1,54 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: writem.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Write OF Item. + +/* + * Generate Po if there is not enough qty + */ + +IF Item.MinQty > + ((Item.OnHand - Item.Allocated) + item.onorder) THEN DO: + + FIND FIRST supplieritemxref where supplieritemxref.itemnum = + item.itemnum NO-LOCK NO-ERROR. + + IF AVAIL supplieritemxref THEN DO: + + FIND supplier WHERE supplier.supplieridnum = + supplieritemxref.supplieridnum NO-LOCK NO-ERROR. + + IF AVAIL supplier THEN DO: + CREATE purchaseorder. + ASSIGN + purchaseorder.DateEntered = today + purchaseorder.POStatus = "Ordered" + purchaseorder.SupplierIDNum = supplieritemxref.supplieridnum. + + CREATE poline. + ASSIGN + poline.ponum = purchaseorder.ponum + poline.linenum = 1 + poline.Discount = supplier.discount + poline.Itemnum = item.itemnum + poline.Price = item.price + poline.qty = item.reorder + poline.ExtendedPrice = (item.price * poline.qty) * (1 - supplier.discount). + item.onorder = item.onorder + item.reorder. + + /****** + MESSAGE "Purchase Order: " purchaseorder.ponum " for " Item.ItemName + ", Item Number: " Item.ItemNum " has been generated." + VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. + *******/ + END. /* If avail suplier*/ + + END. /*if avail supplieritemxref*/ + +END. diff --git a/Sports/AppServer/sports2020trgs/wrord.p b/Sports/AppServer/sports2020trgs/wrord.p new file mode 100644 index 0000000..ad36418 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/wrord.p @@ -0,0 +1,11 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: wrord.p +** Descript: +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER Procedure FOR Write of Order. + diff --git a/Sports/AppServer/sports2020trgs/wrordl.p b/Sports/AppServer/sports2020trgs/wrordl.p new file mode 100644 index 0000000..0263bf1 --- /dev/null +++ b/Sports/AppServer/sports2020trgs/wrordl.p @@ -0,0 +1,32 @@ +/***************************************************************************\ +***************************************************************************** +** +** Program: wrordl.p +** Descript: OrderLine write trigger. +** +***************************************************************************** +\***************************************************************************/ + +TRIGGER PROCEDURE FOR Write OF OrderLine. + +/* Automatically calculate the Extended Price based on Price, Qty, Discount */ + +ExtendedPrice = Price * Qty * (1 - (Discount / 100)). + + +/* In some applications you may want to prohibit the change of a record + * based on the value of some field(s) in that record. This is an example + * of what you might do if you want to prevent the change of an order-line + * record if a ship-date has already been entered for that record. + */ +/* +FIND FIRST order OF orderline NO-ERROR. +IF AVAILABLE order THEN DO: + IF order.shipdate <> ? THEN DO: + MESSAGE "Cannot change an order's detail information when a ship" + "date has been entered for that order." + VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. + RETURN ERROR. + END. +END. +*/ diff --git a/Sports/AppServer/state.i b/Sports/AppServer/state.i new file mode 100644 index 0000000..923c166 --- /dev/null +++ b/Sports/AppServer/state.i @@ -0,0 +1,29 @@ + + /*------------------------------------------------------------------------ + File : State + Purpose : + Syntax : + Description : + Author(s) : Administrator + Created : Fri Jul 27 14:14:58 EDT 2018 + Notes : + ----------------------------------------------------------------------*/ + + /* *************************** Definitions ************************** */ + + /* ******************** Preprocessor Definitions ******************** */ + + /* *************************** Main Block *************************** */ + + /** Dynamically generated schema file **/ + +@openapi.openedge.entity.primarykey (fields="State"). + +DEFINE TEMP-TABLE ttState BEFORE-TABLE bttState +FIELD State AS CHARACTER LABEL "State" +FIELD StateName AS CHARACTER LABEL "State Name" +FIELD Region AS CHARACTER LABEL "Region" +INDEX State IS PRIMARY UNIQUE State ASCENDING . + + +DEFINE DATASET dsState FOR ttState. \ No newline at end of file diff --git a/Sports/PASOEContent/META-INF/MANIFEST.MF b/Sports/PASOEContent/META-INF/MANIFEST.MF new file mode 100644 index 0000000..da0b623 --- /dev/null +++ b/Sports/PASOEContent/META-INF/MANIFEST.MF @@ -0,0 +1,8 @@ +Manifest-Version: 1.0 +Implementation-Version: 12.8.0 +Implementation-Vendor: Progress Software +Implementation-URL: http://www.progress.com +Build-Timestamp: 2023-05-08T07:18:48.723-0400 +Implementation-Title: PAS for OpenEdge ABL Application +ABL-web-application-version: 0.0.0.0 + diff --git a/Sports/PASOEContent/WEB-INF/adapters/rest/README b/Sports/PASOEContent/WEB-INF/adapters/rest/README new file mode 100644 index 0000000..9d43bf2 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/adapters/rest/README @@ -0,0 +1,120 @@ +# Copyright © 1998-2019 Progress Software Corporation and/or one of its subsidiaries or affiliates. All rights reserved. + +This README describes contents of the parent folder "rest". The contents +of parent "rest" folder is used by the runtime to initialize REST transport +for the oeabl service. The layout of the rest folder containing a deployed +service e.g. .paar will look like: + +/rest +| +---- +| | +| ---- .paar +| | +| ---- stagingDir +| | +| ---- Service1.restoe +| | +| ---- mapping.xml +| | +| ---- resourceModel.xml +| | +| ---- spring.xml +| +---- +| . +| . +| . +---- runtime.props + +Each new deployed service will have the same layout as explained for Service1 here. + +1) - Directory + For each Service (as represented by .paar), new directory is created. The name + of the directory bears the name of the Service. This directory contains the + (.paar) and another directory named "stagingDir" which is the inflated version + of the (.paar).The "stagingDir" is created when web application context is initialized + during Tomcat startup. This directory is deleted when the web application context + is destroyed during the Tomcat shutdown. + +2) stagingDir/.paar [Progress Archive] + Archive file containing all the files required to create the REST service endpoint. + Used by REST runtime to create and manage endpoints. DO NOT DELETE. + +3) stagingDir/.restoe + This file contains metadata for the AppServer procedure and the Input/Output params. + EDIT NOT RECOMMENDED. + +4) stagingDir/mapping.xml + This file contains information regarding the mapping of Input/Output Parameters + of the Appserver procedure with the HTTP Request/Response parameters respectively. + EDIT NOT RECOMMENDED. + +5) stagingDir/resourceModel.xml + This file contains information regarding the mapping of AppServer procedure with + the HTTP Verb and HTTP Path. + EDIT NOT RECOMMENDED. + +6) stagingDir/spring.xml + This file serves as seed of all the adapter functionality. The REST adapter runtime + uses this file to load all the mappings and procedure metadata. + EDIT NOT RECOMMENDED. + +7) runtime.props + Property file containing default values of runtime properties. + Each deployed SOAP service will inherit property values for its own copy + of runtime properties from this file. + + +Deploying a PAAR (Progress Archive) in an OEABL Application for PAS Server. +=========================================================================== + +The PAAR maps the Progress 4GL procedures that run on the Application Server. +Deployment of PAAR can be performed using: + +1) deployREST.sh/deployREST.bat utility: + + - The utility reside in the PAS instance's bin folder i.e. {CATALINA_BASE}/bin. + + - General syntax to deploy a paar is: + Examples: + Deploy test.paar to OEABL WebApp named ROOT + # $CATALINA_BASE/bin/deployREST.sh /tmp/test.paar ROOT + + Undeploy an existing REST service named test from OEABL WebApp ROOT + # $CATALINA_BASE/bin/deployREST.sh test ROOT -undeploy + + - Deployment requires a webapp context reload / server restart. To limit + production downtime, deploy all the required REST services first and then + performing Server restart. + + - Once deployed, a new folder is created in + $CATALINA_BASE/webapps//WEB-INF/adapters/rest/. + After the server restart, and successful load of the service by the runtime + [.xml, .restoe ] are created inside the stagingDir. + + +2) REST webservice call to oemanager webapp: + + - oemanager webapp serves as a central utility to manage OEABL webapps. + + - General syntax to deploy a paar is: + Examples: + Deploy test.paar to OEABL WebApp named ROOT + # curl -X POST --data-binary @test.paar + # http://:/oemanager/applications//webapps/ROOT/transports/rest/oeservices + # -H "Accept: application/vnd.progress+json" + # -H "Content-Type: application/vnd.progress.paar+xml" + # -H "Content-Disposition: attachment; filename=test.paar" -v + + Undeploy an existing REST service named test from OEABL WebApp ROOT + # curl -X DELETE + # http://:/oemanager/applications//webapps/ROOT/transports/rest/oeservices/test + + - Once deployed, a new folder is created in + $CATALINA_BASE/webapps//WEB-INF/adapters/rest/. + After the server restart, and successful load of the service by the runtime + [.xml, .restoe ] are created inside the stagingDir. + + - oemanager automatically loads all the new definitions by performing + a reload of the WebpApp by start/stop of the WebApp context. diff --git a/Sports/PASOEContent/WEB-INF/adapters/rest/_oepingService/_oepingService.paar b/Sports/PASOEContent/WEB-INF/adapters/rest/_oepingService/_oepingService.paar new file mode 100644 index 0000000000000000000000000000000000000000..172cda48fd6f1deed6491ee07b425ab211a8239c GIT binary patch literal 2871 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 literal 0 HcmV?d00001 diff --git a/Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props b/Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props new file mode 100644 index 0000000..0f3786f --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props @@ -0,0 +1,21 @@ + + + + + + 1 + + 4 + 0 + 1 + 0 + 1 + 0 + -1 + 1 + 2 + 1 + 0 + 0 + + diff --git a/Sports/PASOEContent/WEB-INF/adapters/soap/README b/Sports/PASOEContent/WEB-INF/adapters/soap/README new file mode 100644 index 0000000..b44400d --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/adapters/soap/README @@ -0,0 +1,71 @@ +# Copyright © 1998-2019 Progress Software Corporation and/or one of its subsidiaries or affiliates. All rights reserved. + +This README describes contents of the parent folder "soap". The contents +of parent "soap" folder is used by the runtime to initialize SOAP transport +for the oeabl service. The layout of the rest folder containing a deployed +service e.g. .paar will look like: + +/soap +| +---- +| | +| ---- .soapoe +| | +| ---- .wsdl +| +---- +| . +| . +| . +---- runtime.props +| . +---- camel-spring.xml + +Deploying a WSM (Web Service Mapping) in an OEABL Appliction for PAS Server. +=========================================================================== + +The WSM maps the Progress 4GL procedures that run on the Application Server. +Deployment of WSM can be performed using: + +1) deploySOAP.sh/deploySOAP.bat utility: + + - The utility reside in the PAS instance's bin folder i.e. {CATALINA_BASE}/bin. + + - General syntax to deploy a wsm is: + Examples: + Deploy test.wsm to OEABL WebApp named ROOT + # $CATALINA_BASE/bin/deploySOAP.sh /tmp/test.wsm ROOT + + Undeploy an existing SOAP service named test from OEABL WebApp ROOT + # $CATALINA_BASE/bin/deploySOAP.sh test ROOT -undeploy + + - While using the utility ensure that the SOAP transport is ENABLED for the + target OEABL Service + + - Once deployed, the above mentioned files [.props, .wsad, .wsdl] will be + created in the $CATALINA_BASE/webapps//WEB-INF/adapters/soap + + +2) REST webservice call to oemanager webapp: + + - oemanager webapp serves as a central utility to manage OEABL webapps. + + - General syntax to deploy a wsm is: + Examples: ( using curl client. You may wish to use any HTTP client ) + Deploy test.wsm to OEABL WebApp named ROOT + # curl -X POST --data-binary @test.wsm + # http://:/oemanager/applications//webapps/ROOT/transports/soap/oeservices + # -H "Accept: application/vnd.progress+json" + # -H "Content-Type: application/vnd.progress.wsm+xml" + # -H "Content-Disposition: attachment; filename=test.wsm" -v + + Undeploy an existing SOAP service named test from OEABL WebApp ROOT + # curl -X POST + # http://:/oemanager/applications//webapps/ROOT/transports/soap/oeservices/test + + - While using the utility ensure that the SOAP transport is ENABLED for the + target OEABL Service + + - Once deployed, the above mentioned files [.soapoe, .wsdl] will be + created in the $CATALINA_BASE/webapps//WEB-INF/adapters/soap/ directory + diff --git a/Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml b/Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml new file mode 100644 index 0000000..4253de9 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml @@ -0,0 +1,21 @@ + + + + + + + com.progress.appserv.adapters.camel.soap.CamelSOAPException + true + + + + + + + + + + \ No newline at end of file diff --git a/Sports/PASOEContent/WEB-INF/adapters/web/README.txt b/Sports/PASOEContent/WEB-INF/adapters/web/README.txt new file mode 100644 index 0000000..adf83fe --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/adapters/web/README.txt @@ -0,0 +1,34 @@ +######################################################### + +The "web" folder will have the WebHandler definition for each Service. For example, if we are going +to create a WebHandler for the service named "SportsSvc" then a folder named "SportsSvc" should +be created and inside that we should place the webhandlers mapping file. + + +The handler configuration should be a JSON Object where each handler definition is a string +inside the JSON Object with the below format. + + "uri":"","class":"handler class name" + + +For ROOT WebApp, ROOT.handlers should be placed inside the "ROOT" folder with no service name +in the handlers file. + +Here is an example of a handlers file + +{ + "version": "2.0", + "serviceName": "PingService", + "handlers": [ + { + "uri":"/_oeping","class":"OpenEdge.Web.PingWebHandler" + "uri":"/pdo/{service}","class":"OpenEdge.Web.DataObject.DataObjectHandler" + } + ] +} + +The URL to access the service should be in the format of /web// +So, for the above example it would be - /web/PingService/_oeping + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml b/Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml new file mode 100644 index 0000000..fdfd2d5 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/apsv-none.xml b/Sports/PASOEContent/WEB-INF/backup/apsv-none.xml new file mode 100644 index 0000000..7374281 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/apsv-none.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml new file mode 100644 index 0000000..8877714 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml new file mode 100644 index 0000000..9809824 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml @@ -0,0 +1,450 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml new file mode 100644 index 0000000..5e83e9a --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml new file mode 100644 index 0000000..2134c8b --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml new file mode 100644 index 0000000..8b852ad --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml @@ -0,0 +1,411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml new file mode 100644 index 0000000..895c4c5 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml @@ -0,0 +1,496 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WEB-INF/meta-psc-oerestadapter-idp-test.xml + + + + + + + + + + + + + + + + WEB-INF/metadata_-_RestSP.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml new file mode 100644 index 0000000..369b7f0 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml @@ -0,0 +1,305 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml new file mode 100644 index 0000000..9a7d03f --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml @@ -0,0 +1,501 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml new file mode 100644 index 0000000..2502adb --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml @@ -0,0 +1,403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml new file mode 100644 index 0000000..9cda4c6 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml @@ -0,0 +1,385 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml new file mode 100644 index 0000000..ead3026 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml @@ -0,0 +1,455 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml new file mode 100644 index 0000000..8a6d4b6 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml @@ -0,0 +1,541 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WEB-INF/meta-psc-oerestadapter-idp-test.xml + + + + + + + + + + + + + + + + WEB-INF/metadata_-_RestSP.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/soap-basic.xml b/Sports/PASOEContent/WEB-INF/backup/soap-basic.xml new file mode 100644 index 0000000..9c3bc87 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/soap-basic.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/backup/soap-none.xml b/Sports/PASOEContent/WEB-INF/backup/soap-none.xml new file mode 100644 index 0000000..51419a9 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/backup/soap-none.xml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/classes/manifest.txt b/Sports/PASOEContent/WEB-INF/classes/manifest.txt new file mode 100644 index 0000000..e69de29 diff --git a/Sports/PASOEContent/WEB-INF/home/ServerStatus.html b/Sports/PASOEContent/WEB-INF/home/ServerStatus.html new file mode 100644 index 0000000..02f5524 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/home/ServerStatus.html @@ -0,0 +1,132 @@ + + + + + +Progress Application Server for OpenEdge + + + + + + + + + + + + +
+
+ + + + + + diff --git a/Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp b/Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp new file mode 100644 index 0000000..6889450 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp @@ -0,0 +1,54 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + isErrorPage="true" + trimDirectiveWhitespaces="true" + errorPage="/WEB-INF/jsp/exceptionPage.jsp" + import="java.io.*" + import="java.util.*" + import="java.system.*" %> + +<%-- Java scriptlet to cleanup raw input properties and attributes used as + psc.as.attr.xxxxx tokens by the HTML template found in this file. --%> + +<%-- Import OWASP --%> +<%@ page import="org.owasp.encoder.Encode" %> + +<%-- Begin editable JSON response template: --%> + +<%@ include file="loadErrorData.jsp" %>{ +"error_code": <%=request.getAttribute("psc.as.attr.errorCode")%> +, "status_txt": "<%=Encode.forHtml((String)request.getAttribute("psc.as.attr.errorMessage"))%>" + +<% if ( (Integer)request.getAttribute("psc.as.attr.detailLevel") > 1) { %> <%-- Add verbose information here --%> +, "error_details": { + "remote_user": "<%=request.getRemoteUser()%>" +, "user_principal": "<%=request.getUserPrincipal()%>" +, "url_scheme": "<%=request.getScheme()%>" +, "remote_addr": "<%=request.getRemoteAddr()%>" +, "server_name": "<%=request.getServerName()%>" +, "product_type": "<%=request.getAttribute("psc.as.attr.product")%>" +, "http_status": <%=(Integer)request.getAttribute("javax.servlet.error.status_code")%> +, "error_detail": "<%=request.getAttribute("psc.as.attr.errorDetail")%>" +} +<% }; %> <%-- End of Error Details --%> + +<% if ( (Integer)request.getAttribute("psc.as.attr.detailLevel") > 2) { %> <%-- Add more debug information here --%> +, "debug_details": { + "http_method": "<%=request.getMethod()%>" +, "web_application": "<%=request.getAttribute("psc.as.attr.webApp")%>" +, "transport": "<%=request.getAttribute("psc.as.attr.transport")%>" +, "request_url": "<%=request.getAttribute("psc.as.attr.requrl")%>" +, "path_info": "<%= Encode.forHtml((String)request.getPathInfo())%>" +, "servlet": "<%=(String)request.getAttribute("javax.servlet.error.servlet_name")%>" +, "uri": "<%=(String)request.getAttribute("javax.servlet.error.request_uri")%>" +, "exception_class": "<%=request.getAttribute("psc.as.attr.exceptionName")%>" +, "exception_message": "<%=request.getAttribute("psc.as.attr.exceptionMessage")%>" +, "exception_stack_trace": "<%=request.getAttribute("psc.as.attr.exceptionStack")%>" +} +<% }; %> + +} +<%-- End editable JSON response template: --%> + diff --git a/Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp b/Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp new file mode 100644 index 0000000..0cc6ea2 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp @@ -0,0 +1,39 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + isErrorPage="true" + trimDirectiveWhitespaces="true" + import="java.io.*" + import="java.util.*" + import="java.system.*" %> +<%@ taglib prefix="sec" uri="/WEB-INF/security.tld" %> +<%@ page import="org.slf4j.Logger,org.slf4j.LoggerFactory" %> +<%! static final Logger logger = LoggerFactory.getLogger("errorPage.jsp"); %> +<% logger.error("Exception in jsp for uri: {}", pageContext.getErrorData().getRequestURI(),pageContext.getException()); %> +<% + String acceptHeader = request.getHeader("accept"); + if ( acceptHeader != null && acceptHeader.matches("^(.*,|)application/(|[\\w\\.]+\\+)json($|,.*|;.*)") ) { + request.setAttribute("psc.as.attr.errorFormat", "json"); + response.setContentType("application/json;charset=UTF-8"); +%> +<%@ include file="errorJSONBody.jsp" %> +<% + } else { + request.setAttribute("psc.as.attr.errorFormat", "html"); +%> + + +Progress Application Server Error + + + + +<%@ include file="errorPageHeader.jsp" %> +<%@ include file="errorPageBody.jsp" %> +<%@ include file="errorPageFooter.jsp" %> + + +<% + } +%> diff --git a/Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp b/Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp new file mode 100644 index 0000000..6ce003c --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp @@ -0,0 +1,54 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + isErrorPage="true" + trimDirectiveWhitespaces="true" + errorPage="/WEB-INF/jsp/exceptionPage.jsp" + import="java.io.*" + import="java.util.*" + import="java.system.*" %> + <%-- Java scriptlet to cleanup raw input properties and attributes used as + psc.as.attr.xxxxx tokens by the HTML template found in this file. --%> +<%@ include file="loadErrorData.jsp" %> + +<%-- Import OWASP --%> +<%@ page import="org.owasp.encoder.Encode" %> + +<%-- Begin Editable HTML page template: --%> +

<%=Encode.forHtml((String)request.getAttribute("psc.as.attr.errorMessage"))%> + +<% if ( (Integer)request.getAttribute("psc.as.attr.detailLevel") > 1) { %> <%-- Add verbose information here --%> +

+ + + + + + + + + + +
Error details
Remote user: <%=request.getRemoteUser()%>
User principal: <%=request.getUserPrincipal()%>
Scheme: <%=request.getScheme()%>
Remote address: <%=request.getRemoteAddr()%>
Server name: <%=request.getServerName()%>
PASOE product type: <%=request.getAttribute("psc.as.attr.product")%>
HTTP status: <%=(Integer)request.getAttribute("javax.servlet.error.status_code")%>
Error detail: <%=request.getAttribute("psc.as.attr.errorDetail")%>
+<% }; %> <%-- End of Error Details --%> + +<% if ( (Integer)request.getAttribute("psc.as.attr.detailLevel") > 2) { %> <%-- Add more debug information here --%> + + + + + + + + + + + + + +

Debug details
HTTP method: <%= request.getMethod()%>
Web application: <%=request.getAttribute("psc.as.attr.webApp")%>
Transport: <%=request.getAttribute("psc.as.attr.transport")%>
Request URL: <%=request.getAttribute("psc.as.attr.requrl")%>
Path info: <%= Encode.forHtml((String)request.getPathInfo())%>
Servlet name: <%=(String)request.getAttribute("javax.servlet.error.servlet_name")%>
URI: <%=(String)request.getAttribute("javax.servlet.error.request_uri")%>
Exception class: <%=request.getAttribute("psc.as.attr.exceptionName")%>
Exception message: <%=request.getAttribute("psc.as.attr.exceptionMessage")%>
Exception stack trace: <%=request.getAttribute("psc.as.attr.exceptionStack")%>
+<% }; %> + +<%-- End Editable HTML page template: --%> + diff --git a/Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp b/Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp new file mode 100644 index 0000000..fc57c3a --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp @@ -0,0 +1,2 @@ +


+<%@ include file="/static/commonPageFooter.html" %> diff --git a/Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp b/Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp new file mode 100644 index 0000000..93d8774 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp @@ -0,0 +1,3 @@ +<%@ include file="/static/commonPageHeader.html" %> +
An error occurred while executing your request!
+
diff --git a/Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp b/Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp new file mode 100644 index 0000000..1d2f19a --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp @@ -0,0 +1,38 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + trimDirectiveWhitespaces="true" + isErrorPage="true" + import="java.io.*" + import="java.util.*" + import="java.system.*" %> + <%! String pageType = "exception"; %> +<% + String acceptHeader = request.getHeader("accept"); + if ( acceptHeader != null && acceptHeader.matches("^(.*,|)application/(|[\\w\\.]+\\+)json($|,.*)") ) { + request.setAttribute("psc.as.attr.errorFormat", "json"); + response.setContentType("application/json;charset=UTF-8"); +%> +<%@ include file="errorJSONBody.jsp" %> +<% + } else { + request.setAttribute("psc.as.attr.errorFormat", "html"); +%> + + +Progress Application Server Exception + + + + +<%@ include file="exceptionPageHeader.jsp" %> +<%@ include file="errorPageBody.jsp" %> +<%@ include file="exceptionPageFooter.jsp" %> + + +<% + } +%> + + diff --git a/Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp b/Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp new file mode 100644 index 0000000..fc57c3a --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp @@ -0,0 +1,2 @@ +

+<%@ include file="/static/commonPageFooter.html" %> diff --git a/Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp b/Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp new file mode 100644 index 0000000..5d0e952 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp @@ -0,0 +1,3 @@ +<%@ include file="/static/commonPageHeader.html" %> +
An application exception occurred while executing your request! +
diff --git a/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties b/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties new file mode 100644 index 0000000..be01107 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties @@ -0,0 +1,25 @@ +code0=Unknown HTTP status code +code400=Bad Request +code401=Unauthorized +code402=reserved +code403=Forbidden +code404=Not Found +code405=Method Not Allowed +code406=Not Acceptable +code407=Proxy Authentication Required +code408=Request Timeout +code409=Conflict +code410=Gone +code411=Length Required +code412=Precondition Failed +code413=Request Entity Too Large +code414=Request-URI Too Long +code415=Unsupported Media Type +code416=Requested Range Not Satisfied +code417=Expectation Failed +code500=Server Error +code501=Not Implemented +code502=Bad Gateway +code503=Service Unavailable +code504=Gateway Timeout +code505=HTTP version Not Supported diff --git a/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties b/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties new file mode 100644 index 0000000..0407f9b --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties @@ -0,0 +1,25 @@ +code0=Unknown HTTP status code +code400=Bad Request: malformed client HTTP request +code401=Unauthorized: authentication failed due to bad credentials +code402=reserved +code403=Forbidden: the server refuses to fulfill the client's request +code404=Not Found: the client requested URI was not found +code405=Method Not Allowed: the method type was not allowed for requested URI +code406=Not Acceptable: the server could not return an acceptable content type +code407=Proxy Authentication Required: client authentication to the proxy server is required +code408=Request Timeout: the client did not send a request in the configured amount of time +code409=Conflict: the server found a conflict in the resource's state +code410=Gone: the requested URI was moved and has no forwarding URL +code411=Length Required: the server requires the HTTP request to supply a Content-length +code412=Precondition Failed: a HTTP request precondition could not be satisfied +code413=Request Entity Too Large: the client request is larger than the server allows +code414=Request-URI Too Long: the client request URI is larger than the server allows +code415=Unsupported Media Type: the client request media-type is not supported +code416=Requested Range Not Satisfied: a client request range header is missing or invalid +code417=Expectation Failed: a client request Expect header cannot be satisfied +code500=Server Error: the server could not produce a response entity due to an internal error +code501=Not Implemented: the server lacks sufficient functionality to execute the request +code502=Bad Gateway: a proxy server detected an invalid response from the the upstream server +code503=Service Unavailable: the requested service is temporarily offline +code504=Gateway Timeout: a proxy server did not recieve a response in the configured amount of time +code505=HTTP version Not Supported: the client's request specified an unsupported HTTP version diff --git a/Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp b/Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp new file mode 100644 index 0000000..39a2ecf --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp @@ -0,0 +1,241 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + isErrorPage="true" + trimDirectiveWhitespaces="true" + errorPage="/WEB-INF/jsp/exceptionPage.jsp" + import="java.io.*" + import="java.util.*" + import="java.system.*" %> + <%-- Java scriptlet to cleanup raw input properties and attributes used as tokens + by the HTML template found at the end of this file. --%> + <% + // Generate the dynamic body content + try { + // Detail level: 0 Use product type defaults + // The following values 1 - 4 override the + // default product type values + // 1 Terse (least information) + // 2 verbose (more information) + // 3 debug (add more jsp page debug info) + String errorMessage = ""; + int detailLevel = 0; + String product = "dev"; + String requrl = "unknown"; + String webAppName = "/ROOT"; + int errorCode = 0; + String errcodeStr = "0"; + String transport = "unknown"; + Throwable appException = null; + String exceptionName = ""; + String exceptionMessage = ""; + String exceptionStack = ""; + String errorDetail = ""; + String statusDetail = ""; + String errorFormat = (String)request.getAttribute("psc.as.attr.errorFormat"); + + // Load the development/production setting for the initial + // detailLevel. + product = System.getProperty("psc.as.security.model"); + if (product == null ) { + product = "default"; + } else { + if ( product.matches("^dev.*") ) { + product = "dev"; + detailLevel = 3; + } else if ( product.matches("^prod.*") ) { + product = "prod"; + detailLevel = 1; + } else { + product = "default"; + detailLevel = 2; + } + } + + // Load any specific detailLevel from the web application env + try { + String sdetailLevel = application.getInitParameter("detailLevel"); + if ( sdetailLevel != null ) { + int cfgDetailLevel = Integer.parseInt(sdetailLevel); + if ( cfgDetailLevel > 0 ) { + detailLevel = cfgDetailLevel; + } + } + } catch(Throwable th) { + } + + // Cleanup the web application name, for the root app a blank + // is returned, but we want to pass in ROOT in that case. + String cp = request.getContextPath(); + if ( cp != null && + cp.length() > 0) { + webAppName = cp; + } + + // Bundle together the error information for writing to the page + if(pageContext != null) { + ErrorData errctx = null; + + // Get the implicit error data object for this request + try { + errctx = pageContext.getErrorData(); + } catch(NullPointerException ne) { + + // Sometimes this call causes a NullPointerException (PageContext.java:514) + // Catch and ignore it.. it effectively means we can't use the ErrorData + } + + // Prepare error report + if(errctx != null) { + // Unload the basic error object fields + requrl = errctx.getRequestURI(); + errorCode = errctx.getStatusCode(); + transport = errctx.getServletName(); + appException = errctx.getThrowable(); + + errorDetail = (String)request.getAttribute("javax.servlet.error.message"); + + // Cleanup some null values with defaults + if ( requrl == null ) requrl = "/"; + if (transport == null ) transport = "default"; + + // The building of the error information is divided into + // HTTP status codes and Exceptions + if ( appException != null ) { + StringBuilder sb = new StringBuilder(); + // Handle application excpetions here + if(appException.getMessage() != null && + appException.getMessage().indexOf("Exception in JSP") != -1) { + sb = new StringBuilder("An error occurred in a JSP file ...\n\n
" + 
+                                    appException.getMessage() + "
"); + } else { + String ecls = ""; + if ( detailLevel > 1 ) { + ecls = appException.getClass().getName() + " ; "; + } + sb = new StringBuilder( ecls + appException.getMessage()); + } + errorDetail = sb.toString().trim(); + + //Cull package name from exception class to avoid revealing structure of program + if(detailLevel < 3) + while(errorDetail.indexOf('.') != -1) + errorDetail = errorDetail.substring(errorDetail.indexOf('.')+1); + + if ( detailLevel > 2 ) { + sb.setLength(0); + StackTraceElement stea[] = appException.getStackTrace(); + if ( errorFormat == null || !errorFormat.equals("json") ) { + // Format for HTML + for (StackTraceElement ste : stea ) { + sb.append("
at "); + sb.append(ste.toString()); + } + } else { + // Format for JSON + String fsep = ""; + sb.append("[ \n"); + for (StackTraceElement ste : stea ) { + sb.append(fsep); + sb.append("\""); + sb.append(ste.toString()); + sb.append("\"\n"); + fsep = ","; + } + sb.append("\n]\n"); + } + exceptionStack = sb.toString().trim(); + } + // Save off individual exception fields rather than needing to + // access the exception built-in object + exceptionName = appException.getClass().getName(); + exceptionMessage = appException.getMessage(); + } + + // Now load error description based on the status code and + // write it to the page + if ( errorCode == 0 ) { + // Sometimes the error object does not include the + // status code ( returns zero ), so try the + // built-in servlet attributes + errcodeStr = (String)request.getAttribute("javax.servlet.error.status_code"); + if ( errcodeStr != null ) { + errorCode = Integer.parseInt(errcodeStr); + } else { + String pageErrorCode = request.getParameter("statusCode"); + if ( pageErrorCode != null ) { + errorCode = Integer.parseInt(pageErrorCode); + } + } + } + + // Load the static HTTP status code description + // Properties file lookup key... + errcodeStr = "code" + Integer.toString(errorCode); + + try { + String descFile = "descFile"; + // Determine which static text file to load from + if (detailLevel < 2 ) { + descFile = "/WEB-INF/jsp/httpCodeDesc-terse.properties"; + } else { + descFile = "/WEB-INF/jsp/httpCodeDesc-verbose.properties"; + } + // Open properties file and load the static + // error code text + InputStream stream = + application.getResourceAsStream(descFile); + Properties props = new Properties(); + props.load(stream); + statusDetail = props.getProperty(errcodeStr); + // if the error code text is not found, then get + // the default error code zero (0) + if ( statusDetail == null ) { + String tmpDetail = props.getProperty("code0"); + if ( tmpDetail == null ) { + tmpDetail = "undefined status code "; + } + statusDetail = tmpDetail + errorCode; + } + } catch ( Throwable thr ) { + statusDetail = "undefined status code description"; + application.log("HTTP status code descriptions not available: " + + thr.toString()); + } + + // Adjust the error message line according to the amount of detail needed + if (detailLevel < 2 ) { + request.setAttribute( "psc.as.attr.errorMessage", statusDetail.trim() ); + } else { + request.setAttribute( "psc.as.attr.errorMessage", errorCode + " - " + + statusDetail.trim() + " - " + request.getMethod() + " " + requrl); + } + + // Set all the other attributes that are accessible via JSP tags + request.setAttribute("psc.as.attr.detailLevel", detailLevel); + request.setAttribute("psc.as.attr.product", product); + request.setAttribute("psc.as.attr.requrl", requrl); + request.setAttribute("psc.as.attr.errorCode", errorCode); + request.setAttribute("psc.as.attr.errorDetail", errorDetail); + request.setAttribute("psc.as.attr.errcodeStr", errcodeStr); + request.setAttribute("psc.as.attr.transport" , transport); + request.setAttribute("psc.as.attr.webApp" , webAppName); + request.setAttribute("psc.as.attr.exceptionName", exceptionName); + request.setAttribute("psc.as.attr.exceptionMessage", exceptionMessage ); + request.setAttribute("psc.as.attr.exceptionStack", exceptionStack ); + + } else { + application.log( "Internal page generation error - no error context available"); + } + } else { + application.log( "Internal page generation error - no request context available"); + } + } catch(Throwable e2) { + + // Error in error handler + application.log("An internal error has occurred in the error page.\n\n"); + application.log("Please copy the following details and provide it to technical send support.\n"); + application.log(e2.toString()); + } +%> diff --git a/Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp b/Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp new file mode 100644 index 0000000..e659569 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp @@ -0,0 +1,37 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + errorPage="/WEB-INF/jsp/errorPage.jsp"%> + + +Progress Application Server Login + + + +<%@ include file="/static/commonPageHeader.html" %> +
+ Progress Application Server user login: +
+<% + String lmodel = (String)application.getAttribute("oeablLoginModel"); + if ( lmodel.matches("form") ) { +%> +
+
+
+
+ +
+<% + } else { +%> +
+ Form login is not compatible with the security policy configuration +<% + } +%> +

+ <%@ include file="/static/commonPageFooter.html" %> + + diff --git a/Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp b/Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp new file mode 100644 index 0000000..a392954 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp @@ -0,0 +1,40 @@ +<%@ page language="java" contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" session="false" + errorPage="/WEB-INF/jsp/errorPage.jsp"%> + + +Progress Application Server logout + + + + <%@ include file="/static/commonPageHeader.html"%> +

+ + + + + +
RemoteUser: <%=request.getRemoteUser()%>
Session: <%=request.getSession(false).getId()%>
RemoteAddr: <%=request.getRemoteAddr()%>
ServerName: <%=request.getServerName()%>
+<% + String lmodel = (String)application.getAttribute("oeablLoginModel"); + if ( lmodel.matches("form") ) { +%> +

+
Do you want to logout this user? +

+ +
+<% + } else { +%> +

+ Form login is not compatible with the security policy configuration +<% + } +%> +

+ <%@ include file="/static/commonPageFooter.html"%> + + + diff --git a/Sports/PASOEContent/WEB-INF/logging.xml b/Sports/PASOEContent/WEB-INF/logging.xml new file mode 100644 index 0000000..506a410 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/logging.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/metadata/README b/Sports/PASOEContent/WEB-INF/metadata/README new file mode 100644 index 0000000..0609a03 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/metadata/README @@ -0,0 +1,13 @@ +This directory is for SAML Service provider & Identity Provider Metadata files. +No other things should/can be deployed in this directory. + +SAML metadata is an XML document which contains information necessary for interaction with SAML -enabled identity or service providers. +The document contains e.g. URLs of endpoints, information about supported bindings, identifiers and public keys. +Typically one metadata document will be generated for your own service provider and sent to all identity providers you want to enable single sign-on with. +Similarly, each identity provider will make its own metadata available for you to import into your service provider application. + +*Service Provider metadata file can be create by direct modification or you can make your application to generate service provider metadata file for you. +(As PAS is not direct login to IDP, so that you could use same SP metadata file that you have created for the PAS client, which configured with + SAML to handle redirect & direct login with IDP) + +*Identity Provider metadata file can be downloaded from configured IdP and put inside your service provider to validate incoming SAML asseration. \ No newline at end of file diff --git a/Sports/PASOEContent/WEB-INF/oeablSecurity.csv b/Sports/PASOEContent/WEB-INF/oeablSecurity.csv new file mode 100644 index 0000000..c16d20e --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/oeablSecurity.csv @@ -0,0 +1,59 @@ +# "url-pattern","","" +############## Intercept-url definitions for the APSV transport URIs ############### +"/apsv/**","HEAD","hasAnyRole('ROLE_PSCUser')" +"/apsv/**","GET","hasAnyRole('ROLE_PSCUser')" +"/apsv/**","POST","hasAnyRole('ROLE_PSCUser')" +"/apsv/**","OPTIONS","hasAnyRole('ROLE_PSCUser')" +############## APSV Container Login model Configuration ############### +#"/apsv/**","OPTIONS","hasAnyRole('ROLE_PSCUser','ROLE_ANONYMOUS')" + +############## Intercept-url definitions for the SOAP transport URIs ############### +"/soap/wsdl/**","GET","hasAnyRole('ROLE_PSCUser')" +"/soap/**","POST","hasAnyRole('ROLE_PSCUser')" + +############## Intercept-url definitions for the REST transport URIs ############### +"/rest/**","*","hasAnyRole('ROLE_PSCUser')" + +############## Intercept-url definitions for the WEB transport URIs ############### +"/web/**","*","hasAnyRole('ROLE_PSCUser')" + +############## Intercept-url definitions for the default URI space ################# +"/static/home.html","GET","hasAnyRole('ROLE_PSCUser')" + +"/static/ServerStatus.html","GET","hasAnyRole('ROLE_PSCUser','ROLE_PSCAdmin','ROLE_PSCDebug')" + +"/server/**","GET","hasAnyRole('ROLE_PSCAdmin','ROLE_PSCDebug')" + +"/*","GET","permitAll()" + +"/static/*","GET","permitAll()" + +"/static/error/*","GET","permitAll()" + +"/static/images/*","GET","permitAll()" + +"/static/auth/*","GET","permitAll()" + +"/static/**","GET","hasAnyRole('ROLE_PSCUser')" + +"/**/*.htm*","GET","hasAnyRole('ROLE_PSCUser')" + +"/**/*.gif","GET","hasAnyRole('ROLE_PSCUser')" + +"/**/*.jpg","GET","hasAnyRole('ROLE_PSCUser')" + +"/**/*.css","GET","hasAnyRole('ROLE_PSCUser')" + +"/**/*.js","GET","hasAnyRole('ROLE_PSCUser')" + +"/**/*.json","GET","hasAnyRole('ROLE_PSCUser')" + +"/**/*.asp","GET","hasAnyRole('ROLE_PSCUser')" + +"/**/*.inc","GET","hasAnyRole('ROLE_PSCUser')" + +# Best practice - deny anything not explicitly granted +"/**","*","denyAll()" + + + diff --git a/Sports/PASOEContent/WEB-INF/oeablSecurity.properties b/Sports/PASOEContent/WEB-INF/oeablSecurity.properties new file mode 100644 index 0000000..b490570 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/oeablSecurity.properties @@ -0,0 +1,656 @@ + ########################################################################## + ## ## + ## Copyright (c) 2018-2023 by Progress Software Corporation ## + ## ## + ## All rights reserved. No part of this program or document may be ## + ## reproduced in any form or by any means without permission in writing ## + ## from Progress Software Corporation. ## + ## ## + ########################################################################## + # + # Spring Security bean properties definition file for a specific oeabl.war + # based web applications found in a PASOE instance + # + # The properties values found in this file constitute a subset of all Spring + # Security configuration properties and their values. Any property value + # declared in this file will supersede a declaration found in any other + # oeablSecurity.properties file in the conf/ or ablapps//conf. + # + # PAS for OE will resolve properties by loading multiple .properties files + # and using the last declared value it finds. The minimum requirement + # is that PAS for OE must find one oeablSecurity.properties file in one + # of the following locations: + # 1) DLC/servers/pasoe/conf/oeablSecurity.properties + # 2) INSTANCE/conf/oeablSecurity.properties + # 3) INSTANCE/ablapps//oeablSecurity.properties + # 4) INSTANCE/webapps//WEB-INF/oeablSecurity.properties <== THIS FILE + # + # The best practice is to declare the properties and values that span + # multiple ABL business applications and their web applications in the + # INSTANCE/conf/oeablSecurity.properties file. + # + # To set a properties and values that apply to all web applications mapped + # to a single ABL business application, create and declare properties in a + # INSTANCE/ablapps//conf/oeablSecurity.properties + # 1) create a directory having the name of the deployed ABL application + # found in the conf/openedge.properties file + # 2) Copy the conf/oeablSecurity.properties into that directory + # 3) Edit the file to contain only the properties that apply to the ABL + # application, leaving the conf/oeablSecurity.properties file to hold + # the defaults + # + # Last, declare the properties and values specific to a web application + # in the INSTANCE/webapps//WEB-INF/oeablSecurity.properties + # + # The web application developer may choose to include the full superset + # of Spring Security properties in their application's + # INSTANCE/ablapps//conf/oeablSecurity.properties + # OR + # INSTANCE/webapps//WEB-INF/oeablSecurity.properties file. + # + # Refer to DLC/servers/pasoe/conf/oeablSecurity.properties for default + # settings. + # + # To use per web application settings, copy the property settings from + # DLC/servers/pasoe/conf/oeablSecurity.properties into this file and set + # the web application specific value. + # + # -------------------- oeablSecurity.properties ------------------------------ + # Detailed descriptions of the properties found in this property file may be + # found in the file: + # + # DLC/servers/pasoe/conf/oeablSecurity.properties.README. + # + ########################################################################## + ## + ## Version: 12.8.0 + ## Date: 2023-05-08 + + ################# Authentication Manager Name list ##################### + ## The following names apply to the authmanager properties in the + ## various transports: + ## http.apsv.authmanager + ## http.soap.authmanager + ## http.rest.authmanager + ## http.web.authmanager + ## http.authmanager + ## + ## Authentication Managers will only apply to loginModels that perform + ## direct logins using user accounts. Therefore, this property is used only + ## for 'basic' and 'form' login models (below) + ## + ## http.all.authmanager will apply the same authentication to all transports. + ## + ## manager name Description + ## =================================================================== + ## local web application WEB-INF/user.properties + ## extlocal web application WEB-INF/user.properties w. + ## encrypted passwords + ## ldap LDAPv3 Directory Service client (simple) + ## ad Microsoft Active Directory Service client + ## oerealm ABL class callback to application accounts + ## sts OpenEdge Authentication Gateway client + ## + ## The http.xxxx.authmanager properties will be ignored for the following + ## client.login.model configurations: + ## oauth2 + ## saml + ## anonymous + ## + http.all.authmanager=local + + ################## The HTTP client Authentication Model to use ########## + ## This property controls which HTTP client authentication model is used + ## between the PASOE client application and the PASOE server application + ## for the . The allowed names are: + ## + ## name Description + ## =================================================================== + ## anonymous No user login - all clients have public access + ## basic Users authenticate using the HTTP BASIC standard + ## form Users authenticate using a HTTP POST message & + ## form data + ## container Users authenticate via Tomcat realm services and + ## authorize URL access via Spring Security + ## sso OpenEdge Single Sign-on using ClientPrincipal + ## access tokens + ## oauth2 OpenEdge support for validating OAuth2 JWT + ## tokens for URL Authorization + ## saml Users authenticate and authorize using SAML token + ## + client.login.model=anonymous + + ################## HTTP BASIC Realm name for All Transports ################## + ## Set the BASIC realm name used by browsers to prompt the user for a + ## user-id/password. + ## + http.all.realm=OpenEdge + + ################## Container (Tomcat) Role mapping properties ################# + ## This property is used by the 'container' Login Model configuration. It + ## provides a [comma separated - no whitespace] list of Role names supplied + ## by the Tomcat realm login token that will be passed through to Spring's + ## URL authorization. + ## + ## Each PAS for OE transport and the default URI space has its own settable + ## list. The property http.jee.all.mappableRoles can be used to set all + ## transports & default at one time. + ## + http.jee.all.mappableRoles=ROLE_PSCUser,ROLE_PSCAdmin,ROLE_PSCOper + + #################APSV Transport Specific Property ####################### + ## For APSV Transport authentication and authorization is disable by default + ## i.e. apsv.security.enable=none. + ## + ## Valid values are: + ## none No HTTP authentication or authorization [default] + ## basic enable HTTP BASIC authentication & role-based authorization + ## container Users authenticate via Tomcat realm services and + ## authorize URL access via Spring Security + ## + apsv.security.enable=none + + #################SOAP Transport Specific property ######################## + ## For SOAP Transport authentication and authorization is disable by default + ## i.e. apsv.security.enable=none. + ## + ## If there is a requirement to enable this in production, then set + ## soap.security.enable property value as "basic". + ## Example: soap.security.enable=basic + ## + ## The http.soap.authprovider property is used to configure the authentication + ## manager as per production need. + ########################################################################## + soap.security.enable=none + + ################# ClientPrincpal creation properties ###################### + ## Properties for the OEClientPrincipalFilter bean + ## This security filter turns an authenticated client's Spring token into an + ## OpenEdge ClientPrincipal object. The filter is thus responsible for: + ## 1. Creating a ClientPrincipal if one was not created in a previous + ## authentication process step + ## 2. If the previous authentication process produced a Spring token - + ## copy that information into the ClientPrincipal + ## 3. If the ClientPrincipal is not already sealed - seal it is using this + ## filter's domain and registry configuration + ## 4. If enablecp is true, then send the ClientPrincipal to the ABL business + ## logic + ## + ## NOTE: The oerealm Authentication Manager configuration does not use these + ## properties. Because of the nature of calling the ABL language, + ## special property handling is required and most of the ClientPrincipal + ## properties are replicated there. + ## + OEClientPrincipalFilter.enabled=true + + ## Location of the encrypted OE Domain Access-codes used to seal Client-Principals + OEClientPrincipalFilter.registryFile=ABLDomainRegistry.keystore + + ## Default OE Domain name to append if not supplied by the user + OEClientPrincipalFilter.domain= + + ## Default ROLE names if none are found for the authenticated user + OEClientPrincipalFilter.roles= + + ## Add authentication process details to Client-Principal + OEClientPrincipalFilter.authz=true + + ## Set Client-Principal token expiration + OEClientPrincipalFilter.expires=0 + + ## Add [any] source user account information to Client-Principal + OEClientPrincipalFilter.accntinfo=false + + ## Generate a Client-Principal for an anonymousUser client + OEClientPrincipalFilter.anonymous=false + + ## Seal a Client-Principal for an anonymousUser client + OEClientPrincipalFilter.sealAnonymous=false + + OEClientPrincipalFilter.appName=OE + + ## Forward an OAuth2 token as a Client-Principal property + OEClientPrincipalFilter.forwardToken=false + + ## Pass an unsealed Client-Principal for an unauthenticated user-id to the + ## ABL application + OEClientPrincipalFilter.passthru=false + + ## Used to obtain an OE Domain name from the authenticated user's list of + ## roles if the user did not supply one (overrides the default domain property) + OEClientPrincipalFilter.domainRoleFilter= + + ## OIDC userinfo properties + OEClientPrincipalFilter.userinfo.servicetype=oidc + OEClientPrincipalFilter.userinfo.url= + + ## LDAP/AD specific: load these user account attributes into the Client-Principal + ## properties + OEClientPrincipalFilter.loadAccntAttrList= + + ## LDAP/AD specific: Validate that the user-id input OE Domain name is valid + ## for the LDAP user account + OEClientPrincipalFilter.validateClientDomain=false + + ## Enable populating Client-Principal user-id attributes + ## with a client's e-mail id instead of their simple user-id and + ## OE Domain name + OEClientPrincipalFilter.useEMailID=false + + ################# CORS security filter properties ######################### + ## Properties for the OECORSFilter Filter bean + ## The security filter that implements the CORS standard for controlling + ## cross site resource access by http clients. + ## + OECORSFilter.responseHeaders=Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,X-CLIENT-CONTEXT-ID + OECORSFilter.allowAll=true + OECORSFilter.allowDomains= + OECORSFilter.allowSubdomains=false + OECORSFilter.allowMethods=GET,POST,PUT,DELETE,OPTIONS,PATCH + OECORSFilter.messageHeaders=Accept,Accept-Language,Content-Language,Content-Type,X-CLIENT-CONTEXT-ID,Origin,Access-Control-Request-Headers,Access-Control-Request-Method,Pragma,Cache-control,Authorization + OECORSFilter.supportCredentials=true + OECORSFilter.maxAge=-1 + + ################# 'sso' Login Model producer/consumer properties ########### + ## Common OpenEdge SSO Producer and Consumer properties + ## (see properties for OESSOTokenManager, OESSOFilter, OESSORefreshFilter ) + OESSO.error.detail=0 + OESSO.require.https=true + + ## Properties for the OESSOTokenManager bean (see authFilters.xml) + ## The OESSOTokenManager bean is the primary component for the verification + ## and generation of OECP SSO tokens. The OESSOTokenManager works a + ## supporting role for other Spring filter beans that are actively involved + ## in the HTTP request authentication process. + ## + OESSOTokenManager.tokenPolicy=disabled + OESSOTokenManager.ssoTokenURLOption=OECP + OESSOTokenManager.ssoTokenExpires=3600 + OESSOTokenManager.ssoAllowScope= + OESSOTokenManager.ssoGrantScope= + OESSOTokenManager.ssoTokenRefresh=true + OESSOTokenManager.ssoRefreshDeltaTime=3600 + OESSOTokenManager.springRolePrefix= + + ## Properties for the OESSOFilter bean (see authFilters.xml) + ## The OESSOFilter bean is injected into the HTTP authentication process + ## to look for HTTP [Authorization] header that contains an OECP SSO token. + ## If no header and token is found, it passes control to the next step + ## in the authentication process. If a header and token is found it will + ## use the OESSOTokenManager to extract, verify, and convert the OECP + ## SSO token into a native OE ClientPrincipal token that is passed to the + ## ABL session. + ## + ## The OESSOFilter bean has properties that control what header to look + ## for in the HTTP request, what the header's authentication-scheme name + ## is, and whether OECP SSO is enabled. + OESSOFilter.authPolicy=disabled + OESSOFilter.authClientType=* + + ## Properties for the OESSORefreshFilter bean (see authFilters.xml) + ## The OESSORefreshFilter bean is injected into the authentication process + ## and intercepts client requests to refresh an expired OECS SSO token. + ## If no request is found, the bean passes the request to the next step + ## in the authentication process. When a request if found, it interfaces + ## with the OESSOTokenManager to validate the refresh request and issue + ## an updated OECP SSO token that has a new expiration date. + ## + ## The OESSORefreshFilter has properties that allow it to recognize when + ## a HTTP request is asking to refresh a SSO token. + OESSORefreshFilter.refreshURL=/static/auth/token + OESSORefreshFilter.refreshClientType=* + + + ########## 'ldap' Authentication Manager properties ####################### + ## Required LDAP Server Authentication Manager configuration properties + + ## Required LDAP URL: {ldap|ldaps}://: + ldap.url=ldap://hostname:389 + + ## Required Directory Server ROOT DN. Unique, per installation, value + ldap.root.dn=dc=acme,dc=com + + ## Required LDAP login user account UPN/DN used to locate the LDAP user + ## account being authenticated and obtain its login ID (i.e. DN) + ## + ## Generic LDAP Server's MUST be an LDAP DN of the user account + ## Example: cn=fred,ou=users,dc=acme,dc=com + ## + ## Active Directory LDAP server MAY be an LDAP DN, OR it may be an + ## UPN (User Principal Name) Example: username@ad-domain + ## + ldap.manager-dn=bsmithf@acme.com + + ## The ldap.manager-password is used with the ldap.manager-dn user account. + ## It has no affect on the user being authenticated. The value may be + ## clear-text or a value generated by the utility: + ## DLC/bin/stspwdutil encrypt + ## + ldap.manager-password=secret + + ## Where and how to being searching for the user's account being authenticated + ldap.usersearch.base= + ldap.usersearch.searchSubtree=true + + ## Default LDAP Server user account search filter. Edit if necessary. + ## ActiveDirectory [default]: (|(userPrincipalName={0}) (sAMAccountName={0}) (mail={0}) (cn={0})) + ## Generic LDAP Directory: (|(mail={0}) (cn={0})) + ldap.usersearch.filter=(|(userPrincipalName={0}) (sAMAccountName={0}) (mail={0}) (cn={0})) + + + ## Where and how to search for the authenticated user's LDAP groups, and how to + ## convert the located LDAP group object's attribute value into a Spring ROLE + ldap.groupsearch.base= + ldap.grouprole.attribute=cn + ldap.groupsearch.searchSubtree=true + + ## Search for the existence of an LDAP group the authenticated LDAP user + ## account has been granted membership in. Each LDAP group object found + ## is used as the Spring/Client-Principal ROLE attributes + ## Active Directory [default]: (&(objectclass=group) (member={0})) + ## Generic LDAP Directory: (|(&(objectclass=groupofnames) (member={0})) (&(objectclass=groupofuniquenames) (uniqueMember={0})) ) + ldap.groupsearch.filter=(&(objectclass=group) (member={0})) + + ##--------------------------------------------------------------------------- + ## Optional LDAP Server Authentication Manager configuration properties + ##--------------------------------------------------------------------------- + + + ## Follow LDAP server referral objects + ldap.contxtSrc.referral=ignore + + ## Connection/read timeout in seconds + ldap.contxtSrc.timeout=5000 + + ## Prefix for LDAP group attribute name to identify it as a Spring ROLE name + ldap.authpopulator.rolePrefix=ROLE_ + + ## Ignore Active Directory exceptions for very large return result-sets + ldap.authpopulator.ignorePartialResultException=true + + ## Convert all LDAP group attribute names used as Spring ROLES to uppercase + ldap.authpopulator.convertToUpperCase=true + + ########## 'oerealm' Authentication Manager properties ####################### + ## Properties that connects to an MS-Agent Realm service and uses it as a + ## source of user account information during the Spring authentication process. + ## These properties are special case versions of the ClientPrincipalFilter + ## property set and only apply to the 'oerealm' Authentication Manager. + ## + OERealm.AuthProvider.multiTenant=true + OERealm.AuthProvider.userDomain= + OERealm.AuthProvider.expires=0 + + ## Encrypted file containing OE Domain Access-codes used to seal + ## Client-Principal tokens + OERealm.AuthProvider.registryFile=ABLDomainRegistry.keystore + + ## Properties for the 'oerealm' Authentication Manager's use of the + ## MS-Agent's OERealm server OOABL class + ## + OERealm.UserDetails.realmClass=OpenEdge.Security.Realm.HybridRealm + OERealm.UserDetails.grantedAuthorities=ROLE_PSCUser + OERealm.UserDetails.appendRealmError=false + OERealm.UserDetails.propertiesAttrName= + OERealm.UserDetails.userIdAttrName= + + ## The file holding a ClientPrincipal token used to authenticate + ## the PASOE server to the OERealm ABL class. + OERealm.UserDetails.realmTokenFile= + + + + ########## 'ad' Authentication Manager properties ######################### + ## Required properties for the 'ad' (Active Directory) Authentication + ## Manager. + ## For all Active Directory authentication manager configuration + ## property details refer to: + ## conf/oeablSecurity.properties.README + ## + ad.ldap.url=ldap://hostname:389 + ad.ldap.rootdn=dc=acme,dc=com + + ## Optional properties for the 'ad' (Active Directory) Authentication + ## Manager. + ## + ad.user.domain=acme.com + ad.AuthoritiesMapper.prefix=ROLE_ + ad.AuthoritiesMapper.convertToUpperCase=true + + ########## 'sts' Authentication Manager properties ############################ + ## OpenEdge Authentication Gateway client configuration + ## for direct user logins to a PASOE server + ## + + ## Required Authentication Gateway URL + sts.UserDetails.stsURL=https://host:port + sts.UserDetails.stsKeystore= + + ## How to handle user-id Domain fields + sts.AuthProvider.multiTenant=true + sts.AuthProvider.registryFile=ABLDomainRegistry.keystore + sts.AuthProvider.userDomain= + + ## TLS connection to Authentication Gateway Server + sts.UserDetails.noHostVerify=false + sts.UserDetails.tlsCipherSuites= + sts.UserDetails.tlsProtocols= + sts.UserDetails.sniHost= + + sts.JwtTokenExchange.stsURL= + sts.JwtTokenExchange.stsKeystore= + sts.JwtTokenExchange.clientHeaderName=x-oests-token + sts.JwtTokenExchange.userAgent=PASOE (Spring) + sts.JwtTokenExchange.certLocation=${psc.as.oe.dlc}/certs + sts.JwtTokenExchange.noHostVerify=false + sts.JwtTokenExchange.tlsCipherSuites= + sts.JwtTokenExchange.tlsProtocols= + sts.JwtTokenExchange.sniHost= + +sts.Saml2TokenExchange.stsURL= +sts.Saml2TokenExchange.stsKeystore= +sts.Saml2TokenExchange.clientHeaderName=x-oests-token +sts.Saml2TokenExchange.userAgent=PASOE (Spring) +sts.Saml2TokenExchange.certLocation=${psc.as.oe.dlc}/certs +sts.Saml2TokenExchange.noHostVerify=true +sts.Saml2TokenExchange.tlsCipherSuites= +sts.Saml2TokenExchange.tlsProtocols= +sts.Saml2TokenExchange.sniHost= + ########## 'oauth2' Login Model properties ############################### + ## Properties for the 'oauth2' Login Model that supplies OAuth2 authorization + ## handling for 'Resource Servers' web data service access and + ## 'Authorization Servers' for obtaining access & refresh tokens to access a + ## Resource server's data. + ## + ## Configuring the 'oauth2' Login Model involves 3 levels: + ## 1) JWT Access/ID token validation (jwtToken.* properties) + ## 2) OAuth2 Resource Server run-time operations and coordination with OAuth2 + ## Authorization Server who issues the JWT tokens + ## (oauth2.resSvc.* properties) + ## 3) Client-Principal generation (see ClientPrincipalFilter.* properties) + ## + + ## Required enable/disable of the OAuth2 Resource server support. + ## The allowable values are {enable} + oauth2.ResourceServer.enable=enable + + ## Required JWT token handler properties for validating the inbound + ## JWT/OAuth2 ID token passed by the OAuth2 client as a Bearer token. + + ## The JWT Signature algorithm used by the Authorization Server + jwtToken.signatureAlg=RS256 + + ## The method of obtaining Public/Secret encryption keys from the + ## Authorization Server. Each type has separate properties + ## following: + jwtToken.keystore.type=jwk + + ## "jwk": URL path to Authorization Server's JWK distribution + jwtToken.keystore.jwkurl=https://localhost:8881/path-to-jwk-set + + ## "jwkissuer": URL path to Authorization Server's provider configuration url + jwtToken.keystore.jwkIssuerUrl=https://idp.example.com + + ## "mac": JWT Signature's mac secret-key phrase + jwtToken.macKey=oeph3::B8E894037D0A296A0908F2FAFB0A0148 + + ## "pkcs12": JWT Signature's public-key storage and access + jwtToken.keystore.path=${catalina.base}/ablapps/${oeabl.ablapp.name}/conf/${oeabl.ablapp.name}.p12 + jwtToken.keystore.userid= + jwtToken.keystore.pwd=oeph0::76E5F6C162276768465F02E4D2D1DDCD + jwtToken.keystore.alias=sample + + ## "jwe": JWT Encryption + jwtToken.keystore.jwe.key.selector=noJWEKeySelector + jwtToken.keystore.jwe.path= + jwtToken.keystore.jwe.pwd= + jwtToken.keystore.jwe.alias= + jwtToken.keystore.jwe.cache=true + + ## After Signature validation, extract key JWT token key assertion values + jwtToken.defaultRoles= + jwtToken.usernameField=sub + jwtToken.mapScopeToRole=true + jwtToken.scopeToRolePrefix=scope. + jwtToken.includeAllClaims=true + jwtToken.scopeNameField=scope + + # Clock skew for validating jwt timestamp + jwtToken.validation.clockSkew=60 + + ## OAuth2 Resource server configuration + ## An OAuth2 Resource server provides data-services for client applications. The client application + ## sends an "access token" obtained from an OAuth2 Authorization server, which the Resource server + ## must validate before it is accepted. Validation involves obtaining a JWT token that can be + ## validated by the Resource server and its "claims" used to perform URL access control to the + ## Resource service's data-services. The JWT may be obtained as a "self-contained" access token + ## or by using the access token as an indirect reference to a JWT stored in the issuing Authorization + ## server. + ## + oauth2.resSvc.tokenServices=oauth2 + oauth2.resSvc.audience=oeablapp + oauth2.resSvc.strictAudience=false + oauth2.resSvc.realmName=oeoauth + oauth2.resSvc.userDetailsPrefix=noOP + oauth2.resSvc.clientId=oeablClient + + ## Configuration for remote validation of tokens (blank uri disables remote validation) + # Client secret may be encrypted using genpassword, e.g. oeph0::76E5F6C162276768465F02E4D2D1DDCD + oauth2.opaqueToken.introspectionUri= + oauth2.opaqueToken.clientSecret= + + ## Configuration for publishing public keys + publicKeys.keystore.path=${catalina.base}/ablapps/${oeabl.ablapp.name}/conf/${oeabl.ablapp.name}.p12 + publicKeys.keystore.pwd=changeme + publicKeys.keystore.aliases=changeme + publicKeys.filter.url=/oauth2/keys + + ########## 'saml' Authention Manager properties ############################ + + ## Location of Service Provider metadata xml file (blank dynamically generates metadata) + # samlToken.metadata.spMetaDataFileLocation=classpath:../metadata/sp.xml + # samlToken.metadata.spMetaDataFileLocation=file:///c:/OpenEdge/WRK/oepas1/webapps/ROOT/WEB-INF/metadata/sp.xml + samlToken.metadata.spMetaDataFileLocation= + + ## Location of Identity Provider metadata xml file + # samlToken.metadata.idpMetaDataFileLocation=https://example.okta.com/app/myappid/sso/saml/metadata + # samlToken.metadata.idpMetaDataFileLocation=file:///c:/OpenEdge/WRK/oepas1/webapps/ROOT/WEB-INF/metadata/idp.xml + samlToken.metadata.idpMetaDataFileLocation=classpath:../metadata/idp.xml + + # URL for retrieving Service Provider metadata + samlToken.metadata.url=/saml2/metadata/${samlToken.serviceProvider.registrationId} + + # comma separated list of audiences + samlToken.validation.validAudiences=${samlToken.serviceProvider.entityId} + + # comma separated list of recipients + # samlToken.validation.validRecipients=${samlToken.serviceProvider.baseUrl}/saml2/login/{samlToken.serviceProvider.registrationId} + samlToken.validation.validRecipients= + + # clock skew expressed as seconds + samlToken.validation.clockSkew=60 + + # validate InResponseTo assertion (default to true) + samlToken.validation.validateInResponseToAssertion=true + + # list of valid saml destination assertions + # if this property is blank, then it defaults to the samlToken.serviceProvider.assertionConsumerServiceLocation + # if both assertionConsumerServiceLocation and validDestinations is blank + # then destination validation is disabled + samlToken.validation.validDestinations= + + # Service provider registration ID + samlToken.serviceProvider.registrationId=${oeabl.ablapp.name} + + # Service provider entity ID + samlToken.serviceProvider.entityId=${samlToken.serviceProvider.baseUrl}${samlToken.metadata.url} + + # Service provider authentication filter URL + samlToken.serviceProvider.authenticationUrl=/saml2/login/${samlToken.serviceProvider.registrationId} + + # Service provider assertion URL + samlToken.serviceProvider.assertionConsumerServiceLocation=${samlToken.serviceProvider.baseUrl}${samlToken.serviceProvider.authenticationUrl} + + # Service provider authentication URL + samlToken.serviceProvider.authenticationRequestUrl=/saml2/authenticate/${samlToken.serviceProvider.registrationId} + + # Service provider redirect parameter name + samlToken.serviceProvider.redirectProperty=RelayState + + # Always use the default target URL + samlToken.serviceProvider.alwaysUseDefaultTargetUrl=false + + # The default target URL + samlToken.serviceProvider.defaultTargetUrl=/ + + ## Allow Binding Methods of incoming request with + ## SAML token + samlToken.httpBinding.allowMethods=GET,POST,PUT,DELETE + + ## SAML UserDetails + ## Usually roles comes as part of assertion attribute of SAML token + ## If there roles are configured with multiple attributes then use comma separated list of attributes + samlToken.UserDetails.roleAttrName=Attribute1,Attribute2 + + ## If there is not roles found in SAMl token then use default roles as PSCUser + samlToken.UserDetails.defaultGrantedAuthorities=PSCUser + samlToken.UserDetails.rolePrefix=ROLE_ + + ## If the SAML token's user-id does not contain a 'domain', use this default + samlToken.UserDetails.userDomain= + + ## Defines additional user authorities using User Details service. + ## Bean name structure: ${samlToken.UserDetails.beanPrefix}UserDetailsService + ## Currently implemented: + ## - noOPUserDetailsService - no UserDetail service + ## - inMemoryUserDetailsService - using user.properties file + samlToken.UserDetails.beanPrefix=noOP + + ## Required location of the OE Domain encrypted Access-code file used to seal + ## Client-Principal tokens with + samlToken.UserDetails.registryFile= + + ## SAML logout properties + #SAML logout URI + samlLogout.request.uri=/saml2/logout + #Successful logout redirect URL (optional) + samlLogout.success.url= + #Successful logout message if logout redirect URL is not set + samlLogout.success.message=Logout executed + #URI SAML IdP provided uses to send logout result token + samlLogout.idp.result.uri=/saml2/logout/result + #Path to keystore to store SAML logout token signature key + samlLogout.keystore.path=${catalina.base}/ablapps/${oeabl.ablapp.name}/conf/${oeabl.ablapp.name}.p12 + #Keystore type defined by samlLogout.keystore.path + samlLogout.keystore.type=PKCS12 + #Keystore path defined by samlLogout.keystore.path + samlLogout.keystore.password=oeph0::76E5F6C162276768465F02E4D2D1DDCD + #Key alias to sign SAML logout token + samlLogout.request.signature.key=defsigkey + + + ########## MDC (Mapped Diagnostic) properties ############################ + ## MDC HTTP request header enablement. Only + ## the HTTP headers included in this list will + ## be available as MDC token fields in ABL SessionManager log files. + MDC.filter.headerList= diff --git a/Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README b/Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README new file mode 100644 index 0000000..0a5e369 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README @@ -0,0 +1,25 @@ +# Copyright © 1998-2019 Progress Software Corporation and/or one of its subsidiaries or affiliates. All rights reserved. + +---------------------- oeablSecurity.properties.README ------------------------- +The oeablSecurity.properties file found in an oeabl web application's WEB-INF/ +directory holds configuration property values that are specific to the web +application. Properties not defined in this web application's +oeablSecurity.properties file are inherited from the full version found in +the PAS for OE instance's conf/ directory. + +The PAS for OE instance's conf/oeablSecurity.properties file holds a full set +of properties and their default values. In the same conf/ directory is the +oeablSecurity.properties.README that describes the full set of properties +supported by the instance. + +You are free to copy property definitions from the conf/ directory's +oeablSecurity.properties file into this web application's version to explicitly +define the property. You may also remove a property from the web application's +oeablSecurity.properties file to begin using the default value found in the +conf/ directory's version. + +To avoid this file's becoming out of date with the PAS for OE instance, +please refer to the the oeablSecurity.properties.README file found in the +conf/ directory. + +--------------------------------------------------------------------------- diff --git a/Sports/PASOEContent/WEB-INF/oeablSecurity.xml b/Sports/PASOEContent/WEB-INF/oeablSecurity.xml new file mode 100644 index 0000000..bae32bb --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/oeablSecurity.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + oeablSecurity.xml + + + + + + + + + + + diff --git a/Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv b/Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv new file mode 100644 index 0000000..f28d459 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv @@ -0,0 +1,49 @@ + +############## Intercept-url definitions for the REST transport URIs ############### +"/rest/**","*","hasAnyAuthority('scope.PSCUser')" + +############## Intercept-url definitions for the WEB transport URIs ############### +"/web/**","*","hasAnyAuthority('scope.PSCUser')" + +############## Intercept-url definitions for the default URI space ################# +"/static/home.html","GET","hasAuthority('scope.PSCUser')" + +"/static/ServerStatus.html","GET","hasAnyAuthority('scope.PSCUser','scope.PSCAdmin','scope.PSCDebug')" + +"/server/**","GET","hasAnyAuthority('scope.PSCAdmin','scope.PSCDebug')" + +"/oauth2/**","GET","permitAll()" + +"/*","GET","permitAll()" + +"/static/*","GET","permitAll()" + +"/static/error/*","GET","permitAll()" + +"/static/images/*","GET","permitAll()" + +"/static/auth/*","GET","permitAll()" + +"/static/**","GET","hasAnyAuthority('scope.PSCUser')" + +"/**/*.htm*","GET","hasAnyAuthority('scope.PSCUser')" + +"/**/*.gif","GET","hasAnyAuthority('scope.PSCUser')" + +"/**/*.jpg","GET","hasAnyAuthority('scope.PSCUser')" + +"/**/*.css","GET","hasAnyAuthority('scope.PSCUser')" + +"/**/*.js","GET","hasAnyAuthority('scope.PSCUser')" + +"/**/*.json","GET","hasAnyAuthority('scope.PSCUser')" + +"/**/*.asp","GET","hasAnyAuthority('scope.PSCUser')" + +"/**/*.inc","GET","hasAnyAuthority('scope.PSCUser')" + +# Best practice - deny anything not explicitly granted +"/**","*","denyAll()" + + + diff --git a/Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt b/Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt new file mode 100644 index 0000000..067c27e --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt @@ -0,0 +1 @@ +This directory can be used to store r-code related specifically to the web application you are deploying. \ No newline at end of file diff --git a/Sports/PASOEContent/WEB-INF/security.tld b/Sports/PASOEContent/WEB-INF/security.tld new file mode 100644 index 0000000..9c3b4b7 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/security.tld @@ -0,0 +1,195 @@ + + + + Spring Security Authorization Tag Library + + 5.2 + security + http://www.springframework.org/security/tags + + + + A tag which outputs the body of the tag if the configured access expression + evaluates to true for the currently authenticated principal. + + authorize + org.springframework.security.taglibs.authz.JspAuthorizeTag + JSP + + + + A Spring-EL expression which is supported by the WebSecurityExpressionHandler + in the application context. The latter will be used to evaluate the expression. + + access + false + true + + + + + A URL within the application. If the user has access to this URL (as determined by + the AccessDecisionManager), the tag body will be evaluated. If not, it will + be skipped. + + url + false + true + + + + + Can be used to specify the HTTP method (typically GET or POST) which is used in combination + with the URL when consulting the AccessDecisionManager. Only has any meaning when used in combination + with the "url" attribute. Defaults to GET. + + method + false + false + + + + + A page scoped variable into which the boolean result of the tag evaluation will be written, allowing the + same condition to be reused subsequently in the page without re-evaluation. + + var + false + false + + + + + + Allows access to the current Authentication object. + + authentication + org.springframework.security.taglibs.authz.AuthenticationTag + empty + + + + Property of the Authentication object which should be output. Supports nested + properties. For example if the principal object is an instance of UserDetails, + the property "principal.username" will return the username. Alternatively, using + "name" will call getName method on the Authentication object directly. + + property + true + true + + + + Name of the exported scoped variable which will contain the + evaluated property of the Authentication object. + + var + false + false + + + + Set HTML escaping for this tag, as a boolean value. + + htmlEscape + false + true + + + + Scope for var. + + scope + false + false + + + + + + Allows inclusion of a tag body if the current Authentication + has one of the specified permissions to the presented + domain object instance. + + accesscontrollist + org.springframework.security.taglibs.authz.AccessControlListTag + JSP + + + + A comma separated list of permissions, which will be converted to + Permission instances by the configured PermissionFactory. + + hasPermission + true + true + + + + The actual domain object instance for which permissions + are being evaluated. + + domainObject + true + true + + + + A page scoped variable into which the boolean result of the tag evaluation will be written, allowing the + same condition to be reused subsequently in the page without re-evaluation. + + var + false + false + + + + + tags, but if for some reason you cannot use + this tag is a handy replacement. You should place this tag within an HTML

block, + where you would normally place other s. Do NOT place this tag within a Spring + block—Spring Security handles Spring forms automatically. + ]]> + csrfInput + org.springframework.security.taglibs.csrf.CsrfInputTag + empty + + + + block, where + you would normally place other meta tags. Once you use this tag, you can access the form field name using + the JQuery $("meta[name='_csrf_parameter']").attr("content") and the header name using + $("meta[name='_csrf_header']").attr("content"). Likewise, you can access the token value with + $("meta[name='_csrf']").attr("content"). You should use a form field when creating and submitting forms from + JavaScript, and you should use a header when sending AJAX requests. If CSRF protection is not enabled, this + tag outputs nothing. + ]]> + csrfMetaTags + org.springframework.security.taglibs.csrf.CsrfMetaTagsTag + empty + + + diff --git a/Sports/PASOEContent/WEB-INF/spring/properties-loader.xml b/Sports/PASOEContent/WEB-INF/spring/properties-loader.xml new file mode 100644 index 0000000..f56c856 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/spring/properties-loader.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + file:${catalina.home}/conf/oeablSecurity.properties + file:${catalina.base}/conf/oeablSecurity.properties + file:${catalina.base}/ablapps/${oeabl.ablapp.name}/conf/oeablSecurity.properties + file:${catalina.base}/webapps/${oeabl.webapp.name}/WEB-INF/oeablSecurity.properties + + + + + diff --git a/Sports/PASOEContent/WEB-INF/tlr/Merge.template b/Sports/PASOEContent/WEB-INF/tlr/Merge.template new file mode 100644 index 0000000..1d32f24 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/tlr/Merge.template @@ -0,0 +1,30 @@ +# See $CATALINA_HOME/conf/openedge.properties.README for details on the properties below. +# DO NOT MODIFY any ${} tags + +# Transport properties for the APSV protocol +[${oepas-app}.${oepas-webapp}.APSV] + adapterEnabled=1 + enableRequestChunking=1 + useHTTPSessions=1 + +# Transport properties for the SOAP protocol +[${oepas-app}.${oepas-webapp}.SOAP] + adapterEnabled=1 + adminEnabled=1 + adminSoapAction=urn:services-progress-com:wsa-admin:01 + debugClients= + wsaUrl=http://${psc.as.host.name}:${psc.as.http.port}/${oepas-webapp}/soap + wsdlEnabled=1 + +# Transport properties for the REST protocol +[${oepas-app}.${oepas-webapp}.REST] + adapterEnabled=1 + +# Transport properties for the WEB protocol +[${oepas-app}.${oepas-webapp}.WEB] + adapterEnabled=1 + defaultCookieDomain= + defaultCookiePath= + defaultHandler=OpenEdge.Web.CompatibilityHandler + srvrDebug=0 + wsRoot=/${oepas-webapp}/static/webspeed diff --git a/Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr b/Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr new file mode 100644 index 0000000..e69de29 diff --git a/Sports/PASOEContent/WEB-INF/users.properties b/Sports/PASOEContent/WEB-INF/users.properties new file mode 100644 index 0000000..7292d17 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/users.properties @@ -0,0 +1,4 @@ +restuser=password,ROLE_PSCUser,enabled +restdebug=password,ROLE_PSCUser,ROLE_PSCAdmin,ROLE_PSCDebug,enabled +restadmin=password,ROLE_PSCUser,ROLE_PSCAdmin,enabled +restnone=password,ROLE_None,enabled diff --git a/Sports/PASOEContent/WEB-INF/web.xml b/Sports/PASOEContent/WEB-INF/web.xml new file mode 100644 index 0000000..716dc2e --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/web.xml @@ -0,0 +1,585 @@ + + + Progress Application Server for OpenEdge + + + + + + + + logback/configuration-resource + java.lang.String + ../logging.xml + + + + + detailLevel + 0 + + + + + + contextConfigLocation + + + + /WEB-INF/oeablSecurity.xml + /WEB-INF/adapters/soap/camel-spring.xml + + + + + + + + contextInitializerClasses + com.progress.appserv.services.security.OESpringPropertySource + + + + + ch.qos.logback.classic.selector.servlet.ContextDetachingSCL + + + + + contextClass + + com.progress.appserv.services.security.OEXmlWebApplicationContext + + + + + + com.progress.appserv.services.security.OESpringContextLoaderListener + + + + + + org.springframework.security.web.session.HttpSessionEventPublisher + + + + + + com.progress.appserv.oeabl.OEAblServletContextListener + + + + + com.progress.appserv.adapters.apsv.OEAiaServletContextListener + + + + com.progress.appserv.adapters.camel.soap.OESoapServletContextListener + + + + com.progress.appserv.adapters.rest.OERestServletContextListener + + + + com.progress.appserv.adapters.web.OEWebServletContextListener + + + + com.progress.appserv.clientrt.broker.SessionLifeCycleListener + + + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + + springSecurityFilterChain + /* + + + + + mvc-dispatch + org.springframework.web.servlet.DispatcherServlet + + contextClass + com.progress.appserv.services.security.OEXmlWebApplicationContext + + + contextConfigLocation + pasoe:mvc-dispatch-context.xml + + 1 + + + + + + + + archiveLocation + WEB-INF/adapters/rest/ + + + + + + + + + + runTimeProperties + WEB-INF/adapters/rest/runtime.props + + + + + + + + + + + + + LoggerContextFilter + ch.qos.logback.classic.selector.servlet.LoggerContextFilter + + + LoggerContextFilter + /* + + + + + OERestAdapterFilter + OERestAdapterFilter + com.progress.appserv.adapters.rest.OERestAdapterFilter + + + OERestAdapterFilter + OERestAdapter + + + + OEAblServletFilter + OEAblServletFilter + com.progress.appserv.oeabl.OEAblServletFilter + + + OEAblServletFilter + /* + + + + + + + + + + + + + + + + + + + + + + ablservice + com.progress.appserv.oeabl.ABLService + 1 + + + + ablservice + /server/* + + + + + APSV Transport Filter + com.progress.appserv.adapters.apsv.PingFilter + + + APSV Transport Filter + apsv + + + + apsv + com.progress.appserv.adapters.apsv.Aia + + instanceName + Aia1 + + 2 + + + + apsv + /apsv/* + + + + + + REST Transport Filter + com.progress.appserv.adapters.rest.PingFilter + + + REST Transport Filter + OERestAdapter + + + + OERestAdapter + com.progress.appserv.adapters.rest.OERestCXFNonSpringServlet + + redirects-list + + .*\.jsp + .*\.jspx + + + + redirect-servlet-name + mvc-dispatch + + + static-resources-list + + .*\.html + .*\.htm + .*\.json + .*\.js + + + 3 + + + + + OERestAdapter + /rest/* + + + + + + WEB Transport Filter + com.progress.appserv.adapters.web.PingFilter + + + WEB Transport Filter + OEWebServlet + + + + OEWebServlet + com.progress.appserv.adapters.web.OEWeb + + redirects-list + + .*\.jsp + .*\.jspx + + + + redirect-servlet-name + mvc-dispatch + + + static-resources-list + + .*\.html + .*\.htm + .*\.json + .*\.js + + + + OEWebHandlerClass + Progress.Web.InternalWebHandler + + 4 + + + + + OEWebURL + /web + + + OEWebServlet + /web/* + + + + + + + + + + + + + soapAdapterLocation + /WEB-INF/adapters/soap/ + + + + + CamelSOAPServlet + + com.progress.appserv.adapters.camel.soap.CamelSOAPServlet + + + ignoreDuplicateServletName + true + + 6 + + + CamelSOAPServlet + /soap/* + + + + + index.html + index.jsp + static/index.html + static/index.jsp + + + + + + + + /WEB-INF/jsp/errorPage.jsp + + + + + java.lang.Throwable + /WEB-INF/jsp/exceptionPage.jsp + + + + + diff --git a/Sports/PASOEContent/WEB-INF/web.xml-clientcert b/Sports/PASOEContent/WEB-INF/web.xml-clientcert new file mode 100644 index 0000000..0d7f1b0 --- /dev/null +++ b/Sports/PASOEContent/WEB-INF/web.xml-clientcert @@ -0,0 +1,583 @@ + + + Progress Application Server for OpenEdge + + + + + + + + logback/configuration-resource + java.lang.String + ../logging.xml + + + + + detailLevel + 0 + + + + + + contextConfigLocation + + + + /WEB-INF/oeablSecurity.xml + /WEB-INF/adapters/soap/camel-spring.xml + + + + + + + + contextInitializerClasses + com.progress.appserv.services.security.OESpringPropertySource + + + + + ch.qos.logback.classic.selector.servlet.ContextDetachingSCL + + + + + contextClass + + com.progress.appserv.services.security.OEXmlWebApplicationContext + + + + + + org.springframework.web.context.ContextLoaderListener + + + + + + org.springframework.security.web.session.HttpSessionEventPublisher + + + + + + com.progress.appserv.oeabl.OEAblServletContextListener + + + + + com.progress.appserv.adapters.apsv.OEAiaServletContextListener + + + + com.progress.appserv.adapters.camel.soap.OESoapServletContextListener + + + + com.progress.appserv.adapters.rest.OERestServletContextListener + + + + com.progress.appserv.adapters.web.OEWebServletContextListener + + + + com.progress.appserv.clientrt.broker.SessionLifeCycleListener + + + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + + springSecurityFilterChain + /* + + + + + mvc-dispatch + org.springframework.web.servlet.DispatcherServlet + + contextClass + com.progress.appserv.services.security.OEXmlWebApplicationContext + + + contextConfigLocation + pasoe:mvc-dispatch-context.xml + + 1 + + + + + + + + archiveLocation + WEB-INF/adapters/rest/ + + + + + + + + + + runTimeProperties + WEB-INF/adapters/rest/runtime.props + + + + + + + + + + + + + LoggerContextFilter + ch.qos.logback.classic.selector.servlet.LoggerContextFilter + + + LoggerContextFilter + /* + + + + + OERestAdapterFilter + OERestAdapterFilter + com.progress.appserv.adapters.rest.OERestAdapterFilter + + + OERestAdapterFilter + OERestAdapter + + + + OEAblServletFilter + OEAblServletFilter + com.progress.appserv.oeabl.OEAblServletFilter + + + OEAblServletFilter + /* + + + + + + + + + + + + + + + + + PASOE oeabl CORS pre-flight constraint + + CORS preflight access + /* + OPTIONS + + + + + PASOE oeabl Security Constraint + + Protected Area + /* + + + ROLE_PSCAdmin + ROLE_PSCOper + ROLE_PSCUser + ROLE_PSCNone + + + + + CLIENT-CERT + OpenEdge + + + + ROLE_PSCAdmin + + + ROLE_PSCOper + + + ROLE_PSCUser + + + ROLE_PSCNone + + + + + + + + ablservice + com.progress.appserv.oeabl.ABLService + 1 + + + + ablservice + /server/* + + + + + APSV Transport Filter + com.progress.appserv.adapters.apsv.PingFilter + + + APSV Transport Filter + apsv + + + + apsv + com.progress.appserv.adapters.apsv.Aia + + instanceName + Aia1 + + 2 + + + + apsv + /apsv/* + + + + + + REST Transport Filter + com.progress.appserv.adapters.rest.PingFilter + + + REST Transport Filter + OERestAdapter + + + + OERestAdapter + com.progress.appserv.adapters.rest.OERestCXFNonSpringServlet + + redirects-list + + .*\.jsp + .*\.jspx + + + + redirect-servlet-name + mvc-dispatch + + + static-resources-list + + .*\.html + .*\.htm + .*\.json + .*\.js + + + 3 + + + + + OERestAdapter + /rest/* + + + + + + WEB Transport Filter + com.progress.appserv.adapters.web.PingFilter + + + WEB Transport Filter + OEWebServlet + + + + OEWebServlet + com.progress.appserv.adapters.web.OEWeb + + redirects-list + + .*\.jsp + .*\.jspx + + + + redirect-servlet-name + mvc-dispatch + + + static-resources-list + + .*\.html + .*\.htm + .*\.json + .*\.js + + + + OEWebHandlerClass + Progress.Web.InternalWebHandler + + 4 + + + + + OEWebURL + /web + + + OEWebServlet + /web/* + + + + + + + + + + + + + soapAdapterLocation + /WEB-INF/adapters/soap/ + + + + + CamelSOAPServlet + + com.progress.appserv.adapters.camel.soap.CamelSOAPServlet + + + ignoreDuplicateServletName + true + + 6 + + + CamelSOAPServlet + /soap/* + + + + + index.html + index.jsp + static/index.html + static/index.jsp + + + + + + + + /WEB-INF/jsp/errorPage.jsp + + + + + java.lang.Throwable + /WEB-INF/jsp/exceptionPage.jsp + + + + + diff --git a/Sports/PASOEContent/favicon.ico b/Sports/PASOEContent/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4b72ec72e65b23565a83a5a0e37c931ded0760f0 GIT binary patch literal 370070 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�E{o*&+f3o)f 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 J*`-$N{{sm_J(~ak literal 0 HcmV?d00001 diff --git a/Sports/PASOEContent/index.jsp b/Sports/PASOEContent/index.jsp new file mode 100644 index 0000000..e9c7b29 --- /dev/null +++ b/Sports/PASOEContent/index.jsp @@ -0,0 +1,32 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + errorPage="/WEB-INF/jsp/errorPage.jsp"%> +<% // Forward to the home page based on being an + // development versus a production server instance + String product = "dev"; + + product = System.getProperty("psc.as.security.model"); + if (product == null ) { + product = "default"; + } + + if ( product.matches("^dev.*") ) { + // For development server instances, redirect the client to the + // static/ServerStatus.html static page (aka static/index.html) + // that the web application tailoring sets up as the home page. + // The redirect is a result of the static page needing the + // correct location for relative URLs to work. + response.sendRedirect(request.getScheme() + "://" + + request.getServerName() + ":" + + request.getServerPort() + + request.getContextPath() + + "/static/index.html"); + } else { + // For production server instance or an untailored default, forward + // directly to the home JSP page because it can dynamically insert + // the necessary URL path elements. + request.getRequestDispatcher("/static/home.jsp").forward(request, response); + } +%> diff --git a/Sports/PASOEContent/static/ServerStatus.html b/Sports/PASOEContent/static/ServerStatus.html new file mode 100644 index 0000000..d7ea3e0 --- /dev/null +++ b/Sports/PASOEContent/static/ServerStatus.html @@ -0,0 +1,132 @@ + + + + + +Progress Application Server for OpenEdge + + + + + + + + + + +
+
+
+
+ Apache Icon +
+
+

+

+
+
+
+
+
+ +
+
Tomcat Version
+
+
+ +
+
PAS Version
+
+
+ +
+
OS Name
+
+
+ +
+
OS Version
+
+
+ +
+
OS Architecture
+
+
+ +
+
+ +
+
JVM Vendor
+
+
+ +
+
JVM Version
+
+
+ +
+
Hostname
+
+
+ +
+
IP Address
+
+
+ +
+
+ +
+ +        + +
+ +
+ +
+ +
+
+ + + + + + diff --git a/Sports/PASOEContent/static/SportsService.json b/Sports/PASOEContent/static/SportsService.json new file mode 100644 index 0000000..592f098 --- /dev/null +++ b/Sports/PASOEContent/static/SportsService.json @@ -0,0 +1,935 @@ +{ + "version": "1.4", + "lastModified": "Wed Feb 06 14:27:17 IST 2019", + "services": [{ + "name": "SportsService", + "address": "\/rest\/SportsService", + "useRequest": true, + "resources": [ + { + "name": "Customer", + "path": "\/Customer", + "autoSave": false, + "schema": { + "type": "object", + "additionalProperties": false, + "properties": {"dsCustomer": { + "type": "object", + "additionalProperties": false, + "properties": {"ttCustomer": { + "type": "array", + "idProperty": "id", + "items": { + "additionalProperties": false, + "properties": { + "_id": { + "type": "string", + "semanticType": "Internal" + }, + "_errorString": { + "type": "string", + "semanticType": "Internal" + }, + "id": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "id" + }, + "seq": { + "type": "integer", + "ablType": "INTEGER", + "default": null, + "title": "seq" + }, + "CustNum": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Cust Num" + }, + "Name": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Name" + }, + "Address": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Address" + }, + "Address2": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Address2" + }, + "Balance": { + "type": "number", + "ablType": "DECIMAL", + "default": 0, + "title": "Balance" + }, + "City": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "City" + }, + "Comments": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Comments" + }, + "Contact": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Contact" + }, + "Country": { + "type": "string", + "ablType": "CHARACTER", + "default": "USA", + "title": "Country" + }, + "CreditLimit": { + "type": "number", + "ablType": "DECIMAL", + "default": 1500, + "title": "Credit Limit" + }, + "Discount": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Discount" + }, + "EmailAddress": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Email" + }, + "Fax": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Fax" + }, + "Phone": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Phone" + }, + "PostalCode": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Postal Code" + }, + "SalesRep": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Sales Rep" + }, + "State": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "State" + }, + "Terms": { + "type": "string", + "ablType": "CHARACTER", + "default": "Net30", + "title": "Terms" + } + } + } + }} + }} + }, + "operations": [ + { + "path": "", + "useBeforeImage": true, + "type": "update", + "verb": "put", + "params": [ + { + "name": "dsCustomer", + "type": "REQUEST_BODY" + }, + { + "name": "dsCustomer", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "", + "useBeforeImage": true, + "type": "delete", + "verb": "delete", + "params": [ + { + "name": "dsCustomer", + "type": "REQUEST_BODY" + }, + { + "name": "dsCustomer", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "?filter={filter}", + "useBeforeImage": true, + "type": "read", + "verb": "get", + "mappingType": "JFP", + "capabilities": "ablFilter,top,skip,id,orderBy", + "params": [ + { + "name": "filter", + "type": "QUERY" + }, + { + "name": "dsCustomer", + "type": "RESPONSE_BODY" + } + ] + }, + { + "name": "count", + "path": "\/count?filter={filter}", + "useBeforeImage": false, + "type": "invoke", + "verb": "put", + "params": [ + { + "name": "filter", + "type": "QUERY" + }, + { + "name": "numRecs", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "", + "useBeforeImage": true, + "type": "create", + "verb": "post", + "params": [ + { + "name": "dsCustomer", + "type": "REQUEST_BODY" + }, + { + "name": "dsCustomer", + "type": "RESPONSE_BODY" + } + ] + }, + { + "name": "SubmitCustomer", + "path": "\/SubmitSCustomer", + "useBeforeImage": true, + "type": "submit", + "verb": "put", + "params": [ + { + "name": "dsCustomer", + "type": "REQUEST_BODY" + }, + { + "name": "dsCustomer", + "type": "RESPONSE_BODY" + } + ] + } + ] + }, + { + "name": "Item", + "path": "\/Item", + "autoSave": false, + "schema": { + "type": "object", + "additionalProperties": false, + "properties": {"dsItem": { + "type": "object", + "additionalProperties": false, + "properties": {"ttItem": { + "type": "array", + "primaryKey": ["Itemnum"], + "items": { + "additionalProperties": false, + "properties": { + "_id": { + "type": "string", + "semanticType": "Internal" + }, + "_errorString": { + "type": "string", + "semanticType": "Internal" + }, + "Itemnum": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Item Num" + }, + "ItemName": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Item Name" + }, + "Price": { + "type": "number", + "ablType": "DECIMAL", + "default": 0, + "title": "Price" + }, + "Onhand": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "On Hand" + }, + "Allocated": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Allocated" + }, + "ReOrder": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Re Order" + }, + "OnOrder": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "On Order" + }, + "CatPage": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Cat Page" + }, + "CatDescription": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Cat-Description" + }, + "Category1": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Category1" + }, + "Category2": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Category2" + }, + "Special": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Special" + }, + "Weight": { + "type": "number", + "ablType": "DECIMAL", + "default": 0, + "title": "Weight" + }, + "Minqty": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Min Qty" + } + } + } + }} + }} + }, + "operations": [ + { + "path": "", + "useBeforeImage": true, + "type": "delete", + "verb": "delete", + "params": [ + { + "name": "dsItem", + "type": "REQUEST_BODY" + }, + { + "name": "dsItem", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "", + "useBeforeImage": true, + "type": "update", + "verb": "put", + "params": [ + { + "name": "dsItem", + "type": "REQUEST_BODY" + }, + { + "name": "dsItem", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "", + "useBeforeImage": true, + "type": "create", + "verb": "post", + "params": [ + { + "name": "dsItem", + "type": "REQUEST_BODY" + }, + { + "name": "dsItem", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "?filter={filter}", + "useBeforeImage": true, + "type": "read", + "verb": "get", + "params": [ + { + "name": "filter", + "type": "QUERY" + }, + { + "name": "dsItem", + "type": "RESPONSE_BODY" + } + ] + }, + { + "name": "SubmitItem", + "path": "\/SubmitItem", + "useBeforeImage": true, + "type": "submit", + "verb": "put", + "params": [ + { + "name": "dsItem", + "type": "REQUEST_BODY" + }, + { + "name": "dsItem", + "type": "RESPONSE_BODY" + } + ] + } + ] + }, + { + "name": "Order", + "path": "\/Order", + "autoSave": false, + "schema": { + "type": "object", + "additionalProperties": false, + "properties": {"dsOrder": { + "type": "object", + "additionalProperties": false, + "properties": {"ttOrder": { + "type": "array", + "primaryKey": ["Ordernum"], + "items": { + "additionalProperties": false, + "properties": { + "_id": { + "type": "string", + "semanticType": "Internal" + }, + "_errorString": { + "type": "string", + "semanticType": "Internal" + }, + "Ordernum": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Order Num" + }, + "CustNum": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Cust Num" + }, + "OrderDate": { + "type": "string", + "ablType": "DATE", + "default": "today", + "title": "Ordered", + "format": "date" + }, + "ShipDate": { + "type": "string", + "ablType": "DATE", + "default": null, + "title": "Shipped", + "format": "date" + }, + "PromiseDate": { + "type": "string", + "ablType": "DATE", + "default": null, + "title": "Promised", + "format": "date" + }, + "Carrier": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Carrier" + }, + "Instructions": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Instructions" + }, + "PO": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "PO" + }, + "Terms": { + "type": "string", + "ablType": "CHARACTER", + "default": "Net30", + "title": "Terms" + }, + "SalesRep": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Sales Rep" + }, + "BillToID": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Bill To ID" + }, + "ShipToID": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Ship To ID" + }, + "OrderStatus": { + "type": "string", + "ablType": "CHARACTER", + "default": "Ordered", + "title": "Order Status" + }, + "WarehouseNum": { + "type": "integer", + "ablType": "INTEGER", + "default": 0, + "title": "Warehouse Num" + }, + "Creditcard": { + "type": "string", + "ablType": "CHARACTER", + "default": "Visa", + "title": "Credit Card" + } + } + } + }} + }} + }, + "operations": [ + { + "path": "", + "useBeforeImage": true, + "type": "create", + "verb": "post", + "params": [ + { + "name": "dsOrder", + "type": "REQUEST_BODY" + }, + { + "name": "dsOrder", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "?filter={filter}", + "useBeforeImage": true, + "type": "read", + "verb": "get", + "params": [ + { + "name": "filter", + "type": "QUERY" + }, + { + "name": "dsOrder", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "", + "useBeforeImage": true, + "type": "delete", + "verb": "delete", + "params": [ + { + "name": "dsOrder", + "type": "REQUEST_BODY" + }, + { + "name": "dsOrder", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "", + "useBeforeImage": true, + "type": "update", + "verb": "put", + "params": [ + { + "name": "dsOrder", + "type": "REQUEST_BODY" + }, + { + "name": "dsOrder", + "type": "RESPONSE_BODY" + } + ] + }, + { + "name": "SubmitOrder", + "path": "\/SubmitOrder", + "useBeforeImage": true, + "type": "submit", + "verb": "put", + "params": [ + { + "name": "dsOrder", + "type": "REQUEST_BODY" + }, + { + "name": "dsOrder", + "type": "RESPONSE_BODY" + } + ] + } + ] + }, + { + "name": "Salesrep", + "path": "\/Salesrep", + "autoSave": false, + "schema": { + "type": "object", + "additionalProperties": false, + "properties": {"dsSalesrep": { + "type": "object", + "additionalProperties": false, + "properties": {"ttSalesrep": { + "type": "array", + "primaryKey": ["SalesRep"], + "items": { + "additionalProperties": false, + "properties": { + "_id": { + "type": "string", + "semanticType": "Internal" + }, + "_errorString": { + "type": "string", + "semanticType": "Internal" + }, + "SalesRep": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Sales Rep" + }, + "RepName": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Rep Name" + }, + "Region": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Region" + }, + "MonthQuota": { + "type": "array", + "maxItems": 12, + "items": {"type": "integer"}, + "ablType": "INTEGER", + "default": 0 + } + } + } + }} + }} + }, + "operations": [ + { + "path": "", + "useBeforeImage": true, + "type": "delete", + "verb": "delete", + "params": [ + { + "name": "dsSalesrep", + "type": "REQUEST_BODY" + }, + { + "name": "dsSalesrep", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "", + "useBeforeImage": true, + "type": "update", + "verb": "put", + "params": [ + { + "name": "dsSalesrep", + "type": "REQUEST_BODY" + }, + { + "name": "dsSalesrep", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "", + "useBeforeImage": true, + "type": "create", + "verb": "post", + "params": [ + { + "name": "dsSalesrep", + "type": "REQUEST_BODY" + }, + { + "name": "dsSalesrep", + "type": "RESPONSE_BODY" + } + ] + }, + { + "name": "SubmitSalesrep", + "path": "\/SubmitSalesrep", + "useBeforeImage": true, + "type": "submit", + "verb": "put", + "params": [ + { + "name": "dsSalesrep", + "type": "REQUEST_BODY" + }, + { + "name": "dsSalesrep", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "?filter={filter}", + "useBeforeImage": true, + "type": "read", + "verb": "get", + "params": [ + { + "name": "filter", + "type": "QUERY" + }, + { + "name": "dsSalesrep", + "type": "RESPONSE_BODY" + } + ] + } + ] + }, + { + "name": "State", + "path": "\/State", + "autoSave": false, + "schema": { + "type": "object", + "additionalProperties": false, + "properties": {"dsState": { + "type": "object", + "additionalProperties": false, + "properties": {"ttState": { + "type": "array", + "primaryKey": ["State"], + "items": { + "additionalProperties": false, + "properties": { + "_id": { + "type": "string", + "semanticType": "Internal" + }, + "_errorString": { + "type": "string", + "semanticType": "Internal" + }, + "State": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "State" + }, + "StateName": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "State Name" + }, + "Region": { + "type": "string", + "ablType": "CHARACTER", + "default": "", + "title": "Region" + } + } + } + }} + }} + }, + "operations": [ + { + "path": "", + "useBeforeImage": true, + "type": "create", + "verb": "post", + "params": [ + { + "name": "dsState", + "type": "REQUEST_BODY" + }, + { + "name": "dsState", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "?filter={filter}", + "useBeforeImage": true, + "type": "read", + "verb": "get", + "params": [ + { + "name": "filter", + "type": "QUERY" + }, + { + "name": "dsState", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "", + "useBeforeImage": true, + "type": "delete", + "verb": "delete", + "params": [ + { + "name": "dsState", + "type": "REQUEST_BODY" + }, + { + "name": "dsState", + "type": "RESPONSE_BODY" + } + ] + }, + { + "name": "SubmitState", + "path": "\/SubmitState", + "useBeforeImage": true, + "type": "submit", + "verb": "put", + "params": [ + { + "name": "dsState", + "type": "REQUEST_BODY" + }, + { + "name": "dsState", + "type": "RESPONSE_BODY" + } + ] + }, + { + "path": "", + "useBeforeImage": true, + "type": "update", + "verb": "put", + "params": [ + { + "name": "dsState", + "type": "REQUEST_BODY" + }, + { + "name": "dsState", + "type": "RESPONSE_BODY" + } + ] + } + ] + } + ] + }] +} \ No newline at end of file diff --git a/Sports/PASOEContent/static/auth/login.html b/Sports/PASOEContent/static/auth/login.html new file mode 100644 index 0000000..8359c4a --- /dev/null +++ b/Sports/PASOEContent/static/auth/login.html @@ -0,0 +1,10 @@ + + +
+
+
+ +
+ + diff --git a/Sports/PASOEContent/static/auth/login.jsp b/Sports/PASOEContent/static/auth/login.jsp new file mode 100644 index 0000000..d0ffc89 --- /dev/null +++ b/Sports/PASOEContent/static/auth/login.jsp @@ -0,0 +1,9 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + errorPage="/WEB-INF/jsp/errorPage.jsp"%> +<% +// Forward to protected login page. +request.getRequestDispatcher("/WEB-INF/jsp/loginPage.jsp").forward(request, response); +%> diff --git a/Sports/PASOEContent/static/auth/loginfail.html b/Sports/PASOEContent/static/auth/loginfail.html new file mode 100644 index 0000000..b2d09a4 --- /dev/null +++ b/Sports/PASOEContent/static/auth/loginfail.html @@ -0,0 +1,6 @@ + + +login failed + + + diff --git a/Sports/PASOEContent/static/auth/logout.html b/Sports/PASOEContent/static/auth/logout.html new file mode 100644 index 0000000..65aed95 --- /dev/null +++ b/Sports/PASOEContent/static/auth/logout.html @@ -0,0 +1,9 @@ + + +
+ +
+ + + diff --git a/Sports/PASOEContent/static/auth/logout.jsp b/Sports/PASOEContent/static/auth/logout.jsp new file mode 100644 index 0000000..bd4870e --- /dev/null +++ b/Sports/PASOEContent/static/auth/logout.jsp @@ -0,0 +1,11 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + errorPage="/WEB-INF/jsp/errorPage.jsp"%> +<% +// Forward to protected logout page. +request.getRequestDispatcher("/WEB-INF/jsp/logoutPage.jsp").forward(request, response); +%> + + diff --git a/Sports/PASOEContent/static/commonPageFooter.html b/Sports/PASOEContent/static/commonPageFooter.html new file mode 100644 index 0000000..014396a --- /dev/null +++ b/Sports/PASOEContent/static/commonPageFooter.html @@ -0,0 +1,6 @@ +
+
+ + diff --git a/Sports/PASOEContent/static/commonPageHeader.html b/Sports/PASOEContent/static/commonPageHeader.html new file mode 100644 index 0000000..f02b8e7 --- /dev/null +++ b/Sports/PASOEContent/static/commonPageHeader.html @@ -0,0 +1,14 @@ + + + diff --git a/Sports/PASOEContent/static/commonStyle.css b/Sports/PASOEContent/static/commonStyle.css new file mode 100644 index 0000000..f3bf549 --- /dev/null +++ b/Sports/PASOEContent/static/commonStyle.css @@ -0,0 +1,275 @@ + +body { + margin: 0px; + background: url("../static/images/background.png") no-repeat center center fixed; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} + +body, button, input, select, textarea { + font-family: Open Sans, Arial, Helvetica, sans-serif; + font-size: 13px; +} + +.button, input[type=button], input[type=submit], input[type=reset], + button { + height: 30px; + background-color: #d94819; + font-weight: 400; + padding: 3px 6px 3px 6px; + cursor: pointer; + background-clip: border-box; + border: 2px solid transparent; + font-size: 13px; + color: white; + text-align: center; + text-shadow: 0px 0px; + text-transform: uppercase; +} + +.button:HOVER, input[type=button]:HOVER, input[type=submit]:HOVER, input[type=reset]:HOVER, + button:HOVER { + background-color: #f54b00; +} + +.button:disabled, input[type=button]:disabled, input[type=submit]:disabled, + input[type=reset]:disabled, button:disabled { + filter: Alpha(opacity = 40); + opacity: .4; + cursor: inherit; +} + +.button.secondary, input[type=button].secondary, input[type=submit].secondary, + input[type=reset].secondary, button.secondary { + background-color: #777777; +} + +.button:HOVER.secondary, input[type=button]:HOVER.secondary, input[type=submit]:HOVER.secondary, + input[type=reset]:HOVER.secondary, button:HOVER.secondary { + background-color: #B1B1B1; +} + +.clear { + clear: both; +} + +.text-center { + text-align: center; +} + +.icon-36 { + width: 36px; + height: 36px; +} + +.icon-48 { + width: 48px; + height: 48px; +} + +.icon-64 { + width: 64px; + height: 64px; +} + +#header { + width: 100%; +} + +#header #ProgressBanner { + height: 39px; + background-color: #1E1F24; + margin: 0; +} + +#header #ProgressBanner #ProgressLogo { + height: 39px; +} + +#header #PASbanner { + height: 48px; + background-color: #56555A; + margin: 0; +} + +#header #PASbanner H2 { + color: #FFF; + font-size: 24px; + font-weight: normal; + margin: 0 0 0 0; + padding: 0; +} + +#header #PASbanner H2 IMG { + vertical-align: -10px; + margin: 7px 5px 0 8px; +} + +#PASmain {width 80%; + margin: 40px 10% 0 10%; +} + +#PASmain #Server { + float: left; + width: 80%; + height: 475px; + border: 1px solid #DDD; + background: #FFF; +} + +#Server #ServerInfo { + width: 100%; + float: left; + padding-top: 15px; + padding-left: 20px; + padding-bottom: 20px; +} + +#Server #ServerInfo #ServerIcon { + float: left; + padding: 5px 15px 0 0; +} + +#Server #ServerInfo #ServerName { + float: left; +} + +#Server #ServerInfo #ServerName H2 { + font-family: "Lato"; + font-size: 32px; + font-weight: normal; + margin: 5px 0 8px 0; +} + +#Server #ServerInfo #ServerName P { + color: #AAA; + font-size: 14px; + margin: 0; +} + +#Server #ServerDetails { + float: left; + width: 100%; +} + +#Server #ServerDetails .column { + float: left; + width: 50%; +} + +#Server #ServerDetails DL { + overflow: auto; + float: left; + width: 100%; + margin: 18px 0 0 20px; +} + +#Server #ServerDetails DL DT, #Server #ServerDetails DL DD { + float: left; + font-size: 13px; +} + +#Server #ServerDetails DL DT { + clear: left; + width: 23%; + color: #AAA; + margin-right: 2%; + font-weight: normal; +} + +#Server #ServerDetails DL DD { + width: 75%; + margin-left: 0; +} + +#Server BUTTON { + margin: 45px 0 30px 0; +} + +#Server BUTTON IMG { + width: 16px; + height: 16px; + vertical-align: -2px; + margin-right: 5px; +} + +#PASmain #Links { + float: left; + width: 19%; + height: 475px; + background-color: #F2F2F2; + border-top: 1px solid #DDD; + border-right: 1px solid #DDD; + border-bottom: 1px solid #DDD; +} + +#PASmain #Links UL { + display: block; + list-style-type: none; + height: 450px; + margin: 0; + padding: 0; +} + +#PASmain #Links UL LI { + display: block; + height: 20%; + border-bottom: 1px solid #DDD; + margin: 0; + padding: 0; +} + +#PASmain #Links UL LI:first-child { + border-top: 0; +} + +#PASmain #Links UL LI:last-child { + border-bottom: 0; +} + +#PASmain #Links UL LI A { + display: block; + width: 100%; + height: 100%; + color: #3F3F3F; + font-size: 14px; + font-weight: bold; + text-decoration: none; + text-align: center; +} + +#PASmain #Links UL LI A:hover { + background: #e8e8e8; +} + +#PASmain #Links UL LI IMG { + display: block; + border: 0; + margin: 0 auto 10px auto; + padding-top: 25px; +} + +#PASmain #Links UL LI SPAN { - + -text-align: center; + padding: 10px 0; +} + +#Footer { + clear: left; + position: fixed; + bottom : 0px; + width: 100%; + height: 30px; + background-color: #373a3f; +} + +#Footer SPAN { + display: block; + float: right; + text-align: right; + color: #FFF; + font-size: 10px; + padding: 10px 12px 0 0; +} \ No newline at end of file diff --git a/Sports/PASOEContent/static/error/error401.html b/Sports/PASOEContent/static/error/error401.html new file mode 100644 index 0000000..fbf9c46 --- /dev/null +++ b/Sports/PASOEContent/static/error/error401.html @@ -0,0 +1,3 @@ + +Unauthorized + diff --git a/Sports/PASOEContent/static/error/error403.html b/Sports/PASOEContent/static/error/error403.html new file mode 100644 index 0000000..143345b --- /dev/null +++ b/Sports/PASOEContent/static/error/error403.html @@ -0,0 +1,3 @@ + +access denied + diff --git a/Sports/PASOEContent/static/error/error404.html b/Sports/PASOEContent/static/error/error404.html new file mode 100644 index 0000000..33d17b3 --- /dev/null +++ b/Sports/PASOEContent/static/error/error404.html @@ -0,0 +1,3 @@ + +Not available + diff --git a/Sports/PASOEContent/static/error/error500.html b/Sports/PASOEContent/static/error/error500.html new file mode 100644 index 0000000..c46a5e8 --- /dev/null +++ b/Sports/PASOEContent/static/error/error500.html @@ -0,0 +1,3 @@ + +Service experienced an internal error. + diff --git a/Sports/PASOEContent/static/error/error503.html b/Sports/PASOEContent/static/error/error503.html new file mode 100644 index 0000000..b0fabe2 --- /dev/null +++ b/Sports/PASOEContent/static/error/error503.html @@ -0,0 +1,3 @@ + +Service is unavailable. + diff --git a/Sports/PASOEContent/static/home.html b/Sports/PASOEContent/static/home.html new file mode 100644 index 0000000..59ddead --- /dev/null +++ b/Sports/PASOEContent/static/home.html @@ -0,0 +1,5 @@ + + +Home + + diff --git a/Sports/PASOEContent/static/home.jsp b/Sports/PASOEContent/static/home.jsp new file mode 100644 index 0000000..6a7da70 --- /dev/null +++ b/Sports/PASOEContent/static/home.jsp @@ -0,0 +1,62 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + trimDirectiveWhitespaces="true" + errorPage="/WEB-INF/jsp/errorPage.jsp"%> + + +Progress Application Home + + +<%@ taglib prefix="sec" uri="/WEB-INF/security.tld" %> + + +<%@ include file="/static/commonPageHeader.html" %> + +

Welcome to Progress Application Server for OpenEdge

+
+Current session: + + + + + +
+<% + String lmodel = application.getInitParameter("contextConfigLocation"); + if ( ! lmodel.matches(".*oeablSecurity-anonymous.*") ) { +%> +<%-- Include login/logout info only for non-anonymous model policies --%> + + + + +
RemoteUser: <%= request.getRemoteUser()%>
Principal: <%= request.getUserPrincipal()%>
RemoteAddr: <%= request.getRemoteAddr()%>
ServerName: <%= request.getServerName()%>

Login: yes
Username:
+ <% if ( lmodel.matches(".*oeablSecurity-form.*") ) { %> +

+ + <% } %> + + +
+ Login: no + Username: + + <% if ( lmodel.matches(".*oeablSecurity-form.*") ) { %> +

+ + <% } %> +
+<% } else { %> +<%-- Show anonymous policy as not/applicable --%> + +
Login: N/A +<% } %> +
+

+<%@ include file="/static/commonPageFooter.html" %> + + diff --git a/Sports/PASOEContent/static/images/Thumbs.db b/Sports/PASOEContent/static/images/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..52708be32ddeb1c1036c47f96158e38011487213 GIT binary patch literal 108032 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 _mGxo?%D^XI;Z4!O^ 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 WeFjQb6L_Pkn&q3*@Bjb)^}hf|B0+ut literal 0 HcmV?d00001 diff --git a/Sports/PASOEContent/static/images/appserver.png b/Sports/PASOEContent/static/images/appserver.png new file mode 100644 index 0000000000000000000000000000000000000000..140c57a84bc90fe3e3bae6f37cd67a2fdbcaec2a GIT binary patch literal 1619 zcmYjRc{J2p9KXT~Sqo*4Buf)Ub~TM`W2Y=l$`%=8?9C%titMsxM3%xajb)xNC`)=W z^iVOjQI-hRjGQbf5AQeQoX)%F-uwN0?&tIUe$Tys+!T9Ta{)d{J^+9K%EH7Eiq6ow zz{3duhpDl~3`97>9LegZ$`(arf23C78aM5m%puS{Mg#rNqyp)4OQk1wzw1q zfM7UlabOi2MxkfFk1|1Cw6JO?9PpMj6}@|6B45W#wA_!Ltet^vG*uAg&Nju?id_4) zyQ^RFp!NAGePX{!X9D9xQA1MXn;PNA=KLSeoaN@N+HUiV7uqr8QZ2bcYD_1z^mBZ) zK5?6tmp8TW$4YyD@1ma74}&MY-Bvg5qnS2difLsL3T6s6PCMDc$3&4|Y*!@uq|4{z z@|yX?kg%p`MI7HiTWDz5@h&^1QTR!VpS#xzy%XW0l~L{$1mzCYb%Wx*RB+*NMOsDi z+1UQu-6Vw>vM!O~@`apPJdo1jH-g4%5gBZ27d zVy%Ubc`bS27jiaBw9An+36`XMcEKJLIPdl%m9sRCA!|EgTOGI~P zbK(*A?#;O@_v(dC+ONIDuPhOK;0}37BVN}^yuKPl`?<$Eyj{HMXcp<&cvFZxC!wZB z#H^%6#TfNGqgsxqN2;XO>k(zp{$w3Dg`(p3T*qxYw@ohuBBf5AMMou8lF5RO3HXuL zIGEf%ON;r^kRwMs#}vB0&-tEgnLjSo88u%Y*N2!d>mH8D~tU0IrlF!@KFDK|d!smN-PD7)uU2qslv)Rq-if zunOa?y!g0aJhPY}lr}c2t8_I;?EUM82D_j1C5P4`*+Kr?Ec1UJmDvY44^0n`Nz2+g z`%Fe~;pXK7%>CsJN7Rd?w3TIFil-ga6LBq|9XPa!keZbe6k5c2V5g{=o>X6*MHf=j zW8v*?hlEG^p5Y#LOJ#L#0j&yD;kJSjCgy$n$y0qB2sw{4(HbqUKO4$8d0kzy48FQg zIje0ZL&R@Vs|GFO8cQmHC0kD^9xF6*NOgYCAo_ox+-7>%t$GX-7Y(W=>4A5dGECXk zE7+Kd*ob)-`2C>2Pk%Nl3lHmQ-74$bfKR8FUaqp^4v5c~DDT1tH@rQ*ISlt#W&X-_ z&dBN&*=+voI{u96ahShJoH7)?Uf=%WwDvwTc{r477{)(KmXSnKnL*h#hw2m}(Kuwrb|y43#7t8m4GNPjI`$SS*~XfL7_-g?tRs8J7L_$Sao(Ei@?6*Zz4z~a?)SOg@B8Dsi?uWng-OF82olAhjjX_q1It>7 z4}y4%4Uc{Xz~g0QVhAx>5R(A-olzDj2r7IivO(Mdcou7JV+?jsTU%R|m6iGV`L(sR zOeQlsJGdRs%l4$9y_U{a|VS%V=&kYb_Ak}tLF`0zdI3;Q85n_64Nts8O*Zs%BtGB zmX?p4?!lqYqvPXWCZ?xnW|x-NH#cAJY`OzR3wxsNydY?o_pim{rdZ4YpPq^_LfN3r z*oh*fz7vw+UH(lt!$Xq1;qgZ+F2U^47%8;S4Z?$S62t3TTcRfIrHe%yXqZ#V+S<~} zC_YxnPN+>mU`{q8B&;K7kX0?XCBwV-qsDoBtr=n*x{>qb%r#R}4=>&ohpVGLso4Q( zHkXIPbxr9@%Ew}rRMVp#qpjnE72gXfp$CNZ6{sZdjnH{qru<7YvK%Jm88y+;ouRnW z@E#wrLv1U=-0K7OuX6*h!;C*?9Qu=HpPSUH9aX6UUnvosE(~ z9Vgft#rQGT)wt280=o7t2(sTL=cZ3aw*qa#_F=5_<;fR38(G24D9Pht%Ux;C)md=w zHuvuS;bQ_iNaUv!_@bS}T8>o5CHhyH_P#i?BHuU4r)l*)J2OP`*c9D&JJ4vnTs=)oMWx)Et_xS??=KEFqQ`xl)N}| zIx{N{A0(TE%xZp-nsB0xA{2LnY=FhN&V-m(e{e*fIB>h_F(1w#CT7HTy8h(N9_+%x z{uh}}oXgKt#b`D4dNu9gFgZ>)L2@B4NHt4O8_$mDo&(nw5_D{+Iqf7%8XzfMuhrXI za{T3s&genBtETH;-=D2u0YSzzKow|{e^4zCq3n^?OD619qq(7 z-Q1yEGQ3XiRK8i6L|Zg-6Lj_e(|oLA!Yv@5L+#afp%2u>;LNCN(g;^JC;nbw^^?kR zI(_l^8w%`;VV_EXF>jFQSvY#67c1R`Lz8+ByLK+)YBc5oMDkko#||D0tXOJkimai{ z!uK%CVeVstJ-LD8g%=JMV$Knfk4%~}pQ>shCub(zc8RIbipc#TTJyWh6f_^FeNAkR zmX;JF-p2^13YnXK4Hw?Xh@KUVjQpX@(wrHoW08)h{6Kc$&qW0dtG;dhepHvG;*GV{ zC<|1m+xSaFMVRb!+(#YWSkuZqIT^<%kodZl%c$>ndCVki$+3p5Sd4fN)b)W&jRvbQX@ zLT99(-F-QL>QVc#U?7*btorTuHo~cwb`!A8k_XB87lS+V#U*rjx3pwL>~F!phh%cz z;*uHqB`QfdXRFXIMdx!qwG!$a@iJtjg_D6*TS88FYhjn@l))9BGWHcBY%P%AAq+E+=O4U-wbydsx_Dhi*wctrZ uq~)~`=JiXyr|NQ^iP9RPn0ctEU>-Hwt|#q}Digq;3&I#%8ZitBA^!s?oHPai literal 0 HcmV?d00001 diff --git a/Sports/PASOEContent/static/images/background.png b/Sports/PASOEContent/static/images/background.png new file mode 100644 index 0000000000000000000000000000000000000000..604e33af0dd273b8ba0fabc9d117e50a0c0d31c5 GIT binary patch literal 243375 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

L!=so=F8vg#c7im+lwv=V!C7Of)g0?;4ra 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 z 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

?a#m!_S5&)Y_ewBRxws0ng{=rc%5dVye{6q` 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 literal 0 HcmV?d00001 diff --git a/Sports/PASOEContent/static/images/communities.png b/Sports/PASOEContent/static/images/communities.png new file mode 100644 index 0000000000000000000000000000000000000000..7e916f90e309dca539a374b1eef337b4f5ac2cb7 GIT binary patch literal 3328 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 literal 0 HcmV?d00001 diff --git a/Sports/PASOEContent/static/images/content.png b/Sports/PASOEContent/static/images/content.png new file mode 100644 index 0000000000000000000000000000000000000000..54bdab52997770b2876ae7206e122a218ec0a5ec GIT binary patch literal 2510 zcmYjSdpuKrA3rWpM7ofMNogd;HgcIu(_$pIC`3_FB170vQbNl;mykig;SY2;N;HU{V1c3ZNs%;k<4!0NX^!0G#K;LrMjbGJ%}JM|l2xCeN?} zfK>u8UKHf;asVb6jVR}D#gp?0d{8i&U-IyBfoc=vymi|)37MTc zckPnj2imWqazOpChK3Fd1~)J?G&+VtVXUm}9UNU;-8{W6UiA$O3dV;=$Hd0PCncw( zWM)0eFDQIoTui2xSI}O)ZftC7YW~pL+SbKjF#G!ZKYwMjMi%?)jxm! z{QcYOb}X8=!nQz+>kR;q4&fgmKh>B003hyfg+@8KSWaXo;C7$b(|V=oJ?O|=In+rl zmy<%<4ioRoYHU43k|3$ScOxE%tJXvJs@JcMtk&bWTt>i)@F3&18|(S$UB*p+e8SlE zkY_F}B(KEz!Z9-iSlq(nqss>7KFGu}&a4U`GyH8>G-W{g{qPJ?3+l}`h_r8=H zcP6^;K%2#{kFqa*w9{tR{u#-lz!bcMJo{{}wPzbgt&+~<+4()3c7&uSVD)OV4CC95 zCCQvOYO_*T^6Hq#Et{s%3sN-{mj>Ej?TvByX4A2h{;M;nHZ!2n$&{r3GE`d6jN^w@ zSJSNeqF9yYZaV~G&WLX%V%$3mmhQCePv~{MQ#`?yF^rMPIpG^q$rSCA9G@Qk%MQ z%iE`qY<3(;+(6fLwf}T3ILT2(5CBOFpRZx5+g0X}GJS=!?Z>dbNn;_(hn*H`Mi=qi zpAq-_tb-pt^INqxs?4-BV(NoOFt^pmQSjZSmU^zAyc>#)mv?@eh)W%$1xJTSb!3ZYA_ClzZB3PG69`Gn!3I?L@Pk)ZY~eJt-s zKVRvN1-o)*ElC#CFX2h@Fa6Kvyab(k5no#E>L+7D3o0|p0#h7gL2iEtavfwjvjM2&~qdR(7h2m&r_{*^yAG&K0P| za+8%fx#NricYAd4I$HPz{4)7_SlYswi4@;)^;{BTw=@`vNcACi^*YHBGyG}3@x_NB zcG`h+MMt$~?#9vK9i=}q*`d}dDQN(D?MLU!4>f;Kp%#3jT`PPIlvMQOKhMvvKstK5X|bHI zTj%F~YvK<4CPc80LO7b1)MNhWfW}bW*cLUCTvhv8zv@QzB@Lh263LYke^h)4s4J;d zj^J8QrvK*qV{+ANB{7sH_cP=|hXsE6fq1ni$iZ&Vr2B<2Cf@-|3EWo%@%>#&Ym(NY z^k<8RRrP<4WmXu=)}`)hg*314(PpZk)=)PGWXh4XHqrWp#O#eN`bYcas^sxtXgVoL zE^Pzv_H!}!BsImm%HC&jprz)vlKoTy>wHWOJX~_+tZx64<&53+Bi%hJkY&82Mm6IC^#BEPBpw{jKJtP=8EX zqqbPJ=W#jwR3dKjY+~bVWUg2xJelhujWHd*cVAzfqq~dt820%ttl`Sdzq&F9*$A_X z$)>+C>Dd~z*6B{89e3pM;~w|UjB97wyltbfrrGks+^5lT*FC}{cXJm#L0^Y7#XHhI zjsLAjzZ|D4!J0qMKVN6EMqeqn4cQdM&Pm#8cUz~h$KJTGTQ!@WKKY$&;Vxn3>9Elq zr%(+`)|YlR^HLscnr5_K^xkni=#NLzKWd0u)E|;*Q6a%OE;F7>quFW;D}653tT**f zkk>ExyaY2IyQ>o}j@krdi~VX&Z(gg}A?sY5p@#*|_0H&LL&-DJ@96To@K=~hntkFP z+y&dDrGf>v&YQ?lyvam4J9fzU`Md%0)I+a^pl5b<=jJO2Vb&z2r;qpHc8uQ2v~woi zdWt=siQgM~y!F9!9>HBKd~cy|g$|M4zh+2VxMtS(>}$zeY)FIgf95^AuH3E-BGZI5_4dOja#XIT)!k$7?lej@ppn25C^^G@LWKyjsAZ>sR4WO%5yXqUKKGTXM{FwZR8i zL#~%E(Fg_zwY@)z>%ocNzv|gWaPUku)7<(2yAJG_iDP8Sw8{OAJpJ?O@t00DE}HA? bL2m%^b%d*xsE-`p&ls>eVT&#|_q_XW$-j($ literal 0 HcmV?d00001 diff --git a/Sports/PASOEContent/static/images/documentation.png b/Sports/PASOEContent/static/images/documentation.png new file mode 100644 index 0000000000000000000000000000000000000000..e69996a24b0a880ce538f4189a706e348bb1c088 GIT binary patch literal 1201 zcmeAS@N?(olHy`uVBq!ia0vp^0YDta!OXzGC^7M(2#~{A9OUlAur|m#Z^_mEKB+I^(p_hFi_EjK9Q(I?^AX}Xbbj%qqa~3#9HRLb?GNj!^)q|6 zwEFg~6*8Kv^WOW?;u??oxJg@oD$81`I_=YM+;06U#x^s;^5y*b4F&#pS6MOa|9CXP zlH>ABiKUJ|`l7WiSg)Eb%aj-=8pIsD%7TS+>LISLn~ur~_X&u{ww%znk8fj3UgCa9 z_|>-?i>?^?zc{?tDR5C$!OR5)&Z=kU>L{hWjLehE_jU2O)X3r#a^s0v%jT6WnJ?TP ztUTiBVR6O(gHYOr?#s4+_bt(QakNhTndhejc8-MB^dPSsbFq2F4NKRlsvhyqdu5dN zv-e)Q)1fyl{FyC*OWd@tbc=mUF*_u%UB^bQR9H{IJ+{R}Mm*zrliZ17TlJ8B9bUPt zv+P%=91~u}b~A5B)bp0MG<(t5=o<{{RXDg#$8yfm(^(TR#XL;ha*Kv-?3!a-?|Mn0qDfV4%#8$PpF@X^3#ltUWVD+wRYaxb`mV2f%~$;ORO7fA z<_mP6h*@TCDS2sT&goaLHaz6uYHHA@a@vmvp`;@GI zcBnQ~c#`kq*K-Sms@$#+%p?YvcYMYh>2&z9W&I+J6A?NTk_vc0@hQxCn9=b66IFlO%m zD;tX9kFP%5W_|rfsrR+;HeN$E90vp4C-{0EgS Mp00i_>zopr0BSz-N&o-= literal 0 HcmV?d00001 diff --git a/Sports/PASOEContent/static/images/gear.png b/Sports/PASOEContent/static/images/gear.png new file mode 100644 index 0000000000000000000000000000000000000000..4937022abcbdea6a62632c2dccf75c2c13a30f3a GIT binary patch literal 17253 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 C0+k5> literal 0 HcmV?d00001 diff --git a/Sports/PASOEContent/static/index.jsp b/Sports/PASOEContent/static/index.jsp new file mode 100644 index 0000000..7fd23b4 --- /dev/null +++ b/Sports/PASOEContent/static/index.jsp @@ -0,0 +1,8 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + errorPage="/WEB-INF/jsp/errorPage.jsp"%> +<% // Forward to the home page +request.getRequestDispatcher("/static/home.jsp").forward(request, response); +%> diff --git a/Sports/PASOEContent/static/serverstatus.js b/Sports/PASOEContent/static/serverstatus.js new file mode 100644 index 0000000..ace972d --- /dev/null +++ b/Sports/PASOEContent/static/serverstatus.js @@ -0,0 +1,78 @@ + + + +tryGetServerInfo(); + +document.getElementById('ManageApps').addEventListener('click', function() { + document.location = '/manager'; +}); + +document.getElementById('ManagePAS').addEventListener('click', function() { + document.location = '/oemanager/'; +}); + +function tryGetUrl(url) { + + var xhr = new XMLHttpRequest(); + xhr.onload = function () { + if (xhr.status !== 200) { + // Let the caller handle a null object return value + // throw new Error(); + updateServerInfo(null); + } else { + var jsonObject = JSON.parse(xhr.responseText); + updateServerInfo(jsonObject); + } + + } + xhr.open('GET', url, true); + xhr.send(null); +} + +function tryGetServerInfo() { + + try { + tryGetUrl('../server'); + } catch (e) { + + } +} + +function updateServerInfo(jsonObject) { + + var apsv = document.getElementById('apsv'); + var soap = document.getElementById('soap'); + var rest = document.getElementById('rest'); + var manager = document.getElementById('manager'); + + if (jsonObject) { + document.getElementById('OEVersion').innerHTML = sanitizeInput(jsonObject.ServerInfo.OEVersion); + document.getElementById('TCVersion').innerHTML = sanitizeInput(jsonObject.ServerInfo.TomcatVersion); + document.getElementById('JVMVendor').innerHTML = sanitizeInput(jsonObject.ServerInfo.JVMVendor); + document.getElementById('JVMVersion').innerHTML = sanitizeInput(jsonObject.ServerInfo.JVMVersion); + document.getElementById('PASVersion').innerHTML = sanitizeInput(jsonObject.ServerInfo.PASVersion); + document.getElementById('OSName').innerHTML = sanitizeInput(jsonObject.ServerInfo.OSName); + document.getElementById('OSVersion').innerHTML = sanitizeInput(jsonObject.ServerInfo.OSVersion); + document.getElementById('OSArch').innerHTML = sanitizeInput(jsonObject.ServerInfo.OSArch); + document.getElementById('HostName').innerHTML = sanitizeInput(jsonObject.ServerInfo.HostName); + document.getElementById('IPAddress').innerHTML = sanitizeInput(jsonObject.ServerInfo.IPAddress); + document.getElementById('upTime').innerHTML = "Server has been running for " + sanitizeInput(jsonObject.UpTime); + } else { + // The serverAlert element does not exist + // document.getElementById('serverAlert').innerHTML = 'Server status + // information is disabled'; + document.getElementById('OEVersion').innerHTML = "OpenEdge"; + document.getElementById('TCVersion').innerHTML = "Unavailable"; + document.getElementById('JVMVendor').innerHTML = "Unavailable"; + document.getElementById('JVMVersion').innerHTML = "Unavailable"; + document.getElementById('PASVersion').innerHTML = "Unavailable"; + document.getElementById('OSName').innerHTML = "Unavailable"; + document.getElementById('OSVersion').innerHTML = "Unavailable"; + document.getElementById('OSArch').innerHTML = "Unavailable"; + document.getElementById('HostName').innerHTML = "Unavailable"; + document.getElementById('IPAddress').innerHTML = "Unavailable"; + document.getElementById('upTime').innerHTML = "Server has been running for: Unavailable"; + } +} + +function sanitizeInput(str) { return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } \ No newline at end of file diff --git a/Sports/PASOEContent/static/sessionsExceeded.jsp b/Sports/PASOEContent/static/sessionsExceeded.jsp new file mode 100644 index 0000000..056f6ea --- /dev/null +++ b/Sports/PASOEContent/static/sessionsExceeded.jsp @@ -0,0 +1,22 @@ +<%@ page language="java" + contentType="text/html;charset=UTF-8" + pageEncoding="UTF-8" + session="false" + isErrorPage="true" + trimDirectiveWhitespaces="true" +%> +<%@ taglib prefix="sec" uri="/WEB-INF/security.tld" %> + + +Progress Application Server Error + + + + + +<%@ include file="../WEB-INF/jsp/errorPageHeader.jsp" %> +This session has been expired (possibly due to multiple concurrent logins being attempted as the same user). +<% response.setStatus(401); %> +<%@ include file="../WEB-INF/jsp/errorPageFooter.jsp" %> + + diff --git a/Sports/build.config b/Sports/build.config new file mode 100644 index 0000000..9420a99 --- /dev/null +++ b/Sports/build.config @@ -0,0 +1,228 @@ +// ====================================================================================================== +// This file contains the project configurations required to build a project. +// +// This enables to extract the project's property settings along with the project so that the +// development and integration environment can use the same configuration. This also enables to use the +// same configuration settings across multiple development workspaces. +// +// This file can be used with 'OpenEdge DevOps Framework(OEDF)' to build the project outside of +// Developer Studio. +// +// For file portability, it is recommended to use variables instead of absolute path for the following +// attributes: +// 'buildDir' +// 'oeide.xrefXmlDir' +// 'oeide.preCompileCallbackRoutine' +// 'avm.wrkDir' +// 'avm.avmOptions.tmpDir' +// 'avm.avmOptions.assembliesDir' +// +// Supported default variables: +// ROOT -> Project's root location +// WRKDIR -> Work directory set during installation +// +// Note: System properties, System environment variables, and Eclipse variables are also supported +// +// Use variable as "${}". For example, +// buildDir="${ROOT}/myBuildDir" +// ====================================================================================================== + + +// ====================================================================================================== +// The OpenEdge DLC path. (Ignored in Developer Studio.) +// This property should be set when building the project using OEDF plugin. +// ====================================================================================================== +// dlcHome="" + +// ====================================================================================================== +// The project's build directory path. +// The directory that will contain saved .r files. If this field is blank, .r files are saved in the +// same directory as the source files. +// ====================================================================================================== +buildDir="${ROOT}/build" + +// IDE specific configurations +oeide { + + // ================================================================================================== + // Boolean value to specify if the project uses a shared AVM or a dedicated AVM. + // If true, the values set in this config file will be ignored and the values defined under + // 'Windows->Preferences->SharedAVM' will be honored. + // ================================================================================================== + useSharedAVM="false" + + // ================================================================================================== + // Boolean value to enable/disable oeide events. + // A named event, oeide_event, is published whenever a Developer Studio operation occurs. This + // enables to write procedures that use the ABL SUBSCRIBE statement to capture and respond to + // those events. + // To know more search for 'Event subscription' at https://www.progress.com/documentation. + // ================================================================================================== + oeideEvents="true" + + // ================================================================================================== + // Boolean value to enable/disable the global Visual Designer toolbox. + // ================================================================================================== + useGlobalToolboxVD="false" + + // ================================================================================================== + // Boolean value to enable/disable passing default AVM parameters. + // When enabled, AVM starts with default parameters specified at + // 'Window->Preferences->Progress OpenEdge->Startup'. + // ================================================================================================== + addDefaultParams="true" + + // ================================================================================================== + // Boolean value to enable/disable project-specific runtime console. + // This setting is applicable when 'useSharedAVM' is false and 'tty.enabled' is true. + // ================================================================================================== + hideTTYConsole="false" + + // ================================================================================================== + // Boolean value to enable/disable use of project specific 'strictOptions' under 'compile'. + // Other compiler options listed under 'compile' section are not affected by this setting. + // ================================================================================================== + useProjectCompilerSettings="false" + + // ================================================================================================== + // Boolean value to enable/disable persisting rcode on compiling sources. + // ================================================================================================== + saveRCode="true" + + // ================================================================================================== + // String value to specify the path where cross-reference information is saved in an XML file. + // This corresponds to the COMPILE option, XREF-XML. + // ================================================================================================== + xrefXmlDir="" + + // ================================================================================================== + // String value to specify the path to a procedure that runs prior to compilation. + // ================================================================================================== + preCompileCallbackRoutine="" +} + +// AVM configurations +// **IMPORTANT** Any change in AVM configuration requires restarting AVM. +avm { + // ================================================================================================== + // String value to specify the path to the working directory, from where the AVM runtime starts. + // ================================================================================================== + wrkDir="${ROOT}" + + // AVM options + avmOptions { + // ============================================================================================== + // String value to specify the path to a temporary directory for the AVM runtime (same as -T + // startup parameter option) + // ============================================================================================== + tmpDir="${WRKDIR}" + + // ============================================================================================== + // Boolean value to use '_progres' or 'prowin' executables. 'true' for _progres, 'false' for prowin. + // ============================================================================================== + tty.enabled="false" + + // ============================================================================================== + // String value to pass startup parameters. This will append to default params if + // 'addDefaultParams' is true. + // ============================================================================================== + startupParameters="" + + // ============================================================================================== + // String value to specify the path to assemblies directory (same as -assemblies startup + // parameter option). + // ============================================================================================== + assembliesDir="" + } + + // ================================================================================================== + // Database configuration (Ignored in Developer Studio.) + // Set the database details when building the project using OEDF plugin. + // database { + // + // } + // ================================================================================================== +} + +// Compile configurations +compile { + // ================================================================================================== + // Comma-separated string values specifying the compilable file extensions. + // This property can be uncommented when building the project using OEDF plugin. + // ================================================================================================== + // compilableFileExtensions="p,w,cls,pgen,html,htm" + + compileOptions { + // ============================================================================================== + // Boolean value to set COMPILER:MULTI-COMPILE attribute. + // ============================================================================================== + multiCompile.enabled="false" + + // Strict options + strictOptions { + // ========================================================================================== + // Similar to 'OPTIONS require-full-names' in COMPILE statement. + // Value can be 'ignore/warning/error' + // ========================================================================================== + requireFullNames="Ignore" + + // ========================================================================================== + // Similar to 'OPTIONS require-field-qualifiers' in COMPILE statement. + // Value can be 'ignore/warning/error'. + // ========================================================================================== + requireFieldQualifiers="Ignore" + + // ========================================================================================== + // Similar to 'OPTIONS require-full-keywords' in COMPILE statement. + // Value can be 'ignore/warning/error'. + // ========================================================================================== + requireFullKeywords="Ignore" + + // ========================================================================================== + // Similar to 'OPTIONS require-return-values' in COMPILE statement. + // Value can be 'ignore/warning/error'. + // ========================================================================================== + requireReturnValues="Ignore" + } + + // ============================================================================================== + // String value to specify XCODE option in COMPILE statement. + // ============================================================================================== + xcodeKey="" + + // ============================================================================================== + // Boolean value to set XREF-XML option in COMPILE statement. + // ============================================================================================== + xrefXml.enabled="false" + + // ============================================================================================== + // Boolean value to set STREAM-IO option in COMPILE statement. + // ============================================================================================== + streamIO.enabled="false" + + // LANGUAGES option in COMPILE statement. + languages { + // ========================================================================================== + // A comma-separated list of language segments to include in the compiled r-code. + // ========================================================================================== + list="" + + // ========================================================================================== + // TEXT-SEG-GROW option. Growth-factor as an integer. Supported only when the language list + // is provided. + // ========================================================================================== + textSegGrow="" + } + + // ============================================================================================== + // Boolean value to set MIN-SIZE option in COMPILE statement. + // ============================================================================================== + minSize.enabled="false" + + // ============================================================================================== + // Boolean value to set ATTR-SPACE option in COMPILE statement. + // (Not supported in OEDF) + // ============================================================================================== + attrSpace.enabled="false" + } +} diff --git a/Sports/conf/MANIFEST.MF b/Sports/conf/MANIFEST.MF new file mode 100644 index 0000000..8651f82 --- /dev/null +++ b/Sports/conf/MANIFEST.MF @@ -0,0 +1,3 @@ +version: 1.0.0 +name: Sports +deployment-type: ablapp \ No newline at end of file diff --git a/Sports/conf/startup.pf b/Sports/conf/startup.pf new file mode 100644 index 0000000..c380391 --- /dev/null +++ b/Sports/conf/startup.pf @@ -0,0 +1 @@ +-db sports -H oedbmachine -S 7654 \ No newline at end of file diff --git a/Sports/tests/AppServer/CustomerTest.cls b/Sports/tests/AppServer/CustomerTest.cls new file mode 100644 index 0000000..3f4a8b6 --- /dev/null +++ b/Sports/tests/AppServer/CustomerTest.cls @@ -0,0 +1,162 @@ + +/*------------------------------------------------------------------------ + File : CustomerTest + Purpose : + Syntax : + Description : + Author(s) : rahulk + Created : Sat Sep 09 16:43:47 IST 2023 + Notes : + ----------------------------------------------------------------------*/ + +USING Progress.Lang.*. +USING OpenEdge.Core.Assert FROM PROPATH. + +BLOCK-LEVEL ON ERROR UNDO, THROW. + +CLASS CustomerTest: + + /* DEFINE TEMP-TABLE ttOrder */ + /* FIELD Ordernum AS INTEGER LABEL "Order Num" FORMAT "zzzzzzzzz9" INITIAL "0".*/ + /* DEFINE DATASET dsCustomer1 FOR ttOrder. */ + /* + {} */ + +/* {customer.i}*/ + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @Before. + METHOD PUBLIC VOID setUpBeforeClass( ): + + RETURN. + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @Setup. + METHOD PUBLIC VOID setUp( ): + + RETURN. + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @TearDown. + METHOD PUBLIC VOID tearDown( ): + + RETURN. + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @After. + METHOD PUBLIC VOID tearDownAfterClass( ): + + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testReadCustomer( ): +/* DEFINE VARIABLE customerVar AS Customer NO-UNDO. */ +/* DEFINE VARIABLE iQuery AS CHARACTER NO-UNDO. */ +/* */ +/* customerVar = NEW Customer(). */ +/* iQuery="WHERE Customer.State = 'UNKNOWN'". */ +/* */ +/* DEFINE DATA-SOURCE srcCustomer FOR Customer. */ +/* /* DEFINE BUFFER OtherCust FOR Customer. */ */ +/* /* */ */ +/* /* DATASET dsCustomer:FILL(). */ */ +/* /* */ */ +/* /* DATASET dsCustomer:HANDLE. */ */ +/* */ +/* BUFFER ttCustomer:ATTACH-DATA-SOURCE(DATA-SOURCE srcCustomer:HANDLE,*/ +/* "Customer.Name,CustName"). */ +/* */ +/* customerVar:ReadCustomer(iQuery, DATASET dsCustomer). */ +/* */ +/* DATASET dsCustomer:FILL(). */ +/* BUFFER ttCustomer:DETACH-DATA-SOURCE(). */ +/* FOR EACH ttCustomer: */ +/* DISPLAY */ +/* ttCustomer.Name */ +/* ttCustomer.State. */ +/* Assert:Equals(ttCustomer.State, "hey"). */ +/* */ +/* END. */ + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testCreateCustomer( ): + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testcount( ): + DEFINE VARIABLE customerVar AS Customer NO-UNDO. + DEFINE VARIABLE iQuery AS CHARACTER NO-UNDO. + DEFINE VARIABLE oCount AS INTEGER NO-UNDO. + + customerVar = NEW Customer(). + iQuery="WHERE Customer.State = 'UNKNOWN'". + customerVar:count(iQuery, oCount). + Assert:Equals(oCount , 0). + + iQuery="WHERE Customer.State = 'NH'". + customerVar:count(iQuery, oCount). + Assert:NotEqual(oCount , 0). + Assert:IsPositive(oCount). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testDeleteCustomer( ): + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + +END CLASS. \ No newline at end of file diff --git a/Sports/tests/AppServer/OrderTest.cls b/Sports/tests/AppServer/OrderTest.cls new file mode 100644 index 0000000..ff81736 --- /dev/null +++ b/Sports/tests/AppServer/OrderTest.cls @@ -0,0 +1,43 @@ + +/*------------------------------------------------------------------------ + File : OrderTest + Purpose : + Syntax : + Description : + Author(s) : rahulk + Created : Sat Sep 09 19:40:47 IST 2023 + Notes : + ----------------------------------------------------------------------*/ + +USING Progress.Lang.*. +USING OpenEdge.Core.Assert FROM PROPATH. + +BLOCK-LEVEL ON ERROR UNDO, THROW. + +CLASS OrderTest: + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testCreateOrder( ): + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testReadOrder( ): + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + +END CLASS. \ No newline at end of file diff --git a/Sports/tests/AppServer/SportsTestSuite.cls b/Sports/tests/AppServer/SportsTestSuite.cls new file mode 100644 index 0000000..516839c --- /dev/null +++ b/Sports/tests/AppServer/SportsTestSuite.cls @@ -0,0 +1,16 @@ + + /*------------------------------------------------------------------------ + File : SportsTestSuite + Syntax : + Author(s) : rahulk + Created : Sat Sep 09 16:41:21 IST 2023 + Notes : + ----------------------------------------------------------------------*/ + +USING Progress.Lang.*. +BLOCK-LEVEL ON ERROR UNDO, THROW. + +@TestSuite(classes= "CustomerTest, OrderTest"). +CLASS SportsTestSuite: + +END CLASS. \ No newline at end of file diff --git a/Sports/tlr/merge.properties b/Sports/tlr/merge.properties new file mode 100644 index 0000000..3949d0b --- /dev/null +++ b/Sports/tlr/merge.properties @@ -0,0 +1,52 @@ +# ABL App props +[Sports] + webApps=Sports + +[AppServer.Agent.Sports] + numInitialSessions=2 + PROPATH=${CATALINA_BASE}/webapps/Sports/WEB-INF/openedge,${CATALINA_BASE}/ablapps/Sports/openedge,${CATALINA_BASE}/openedge,${DLC}/tty,${DLC}/tty/netlib/OpenEdge.Net.apl + uuid=http://NBHYDRAHULK11:/Sports + +[AppServer.SessMgr.Sports] + agentStartupParam=-T "${CATALINA_BASE}/temp" -pf ${CATALINA_BASE}/ablapps/Sports/conf/startup.pf + agentLogEntryTypes=ASPlumbing,DB.Connects + agentLogFile=${catalina.base}/logs/SportsApp.agent.{yyyy-mm-dd}.log + +# Web App props +[Sports.Sports] + allowRuntimeUpdates=1 + collectMetrics=1 + statusEnabled=1 + +# See $CATALINA_HOME/conf/openedge.properties.README for details on the properties below. +# DO NOT MODIFY any ${} tags +# Transport properties for the APSV protocol +[Sports.Sports.APSV] + adapterEnabled=1 + enableRequestChunking=1 + oepingEnabled=0 + oepingProcedure= + serviceFaultLevel=1 + useHTTPSessions=1 + +# Transport properties for the SOAP protocol +[Sports.Sports.SOAP] + adapterEnabled=1 + adminEnabled=1 + adminSoapAction=urn:services-progress-com:wsa-admin:01 + debugClients= + wsaUrl=http://${psc.as.host.name}:${psc.as.http.port}/Sports/soap + wsdlEnabled=1 + +# Transport properties for the REST protocol +[Sports.Sports.REST] + adapterEnabled=1 + +# Transport properties for the WEB protocol +[Sports.Sports.WEB] + adapterEnabled=1 + defaultCookieDomain= + defaultCookiePath= + defaultHandler=OpenEdge.Web.CompatibilityHandler + srvrDebug=0 + wsRoot=/Sports/static/webspeed \ No newline at end of file diff --git a/Sports/tlr/openedge.properties.merge b/Sports/tlr/openedge.properties.merge new file mode 100644 index 0000000..6cfdcc0 --- /dev/null +++ b/Sports/tlr/openedge.properties.merge @@ -0,0 +1,2 @@ +[AppServer.SessMgr.${oepas-app}] + agentStartupParam=-T "${CATALINA_BASE}/temp" -pf ${CATALINA_BASE}/ablapps/${oepas-app}/conf/startup.pf From b223d04a2a8d4ab8eaa2f6f77879a9f6b1633a1b Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 13 Sep 2023 18:25:45 +0530 Subject: [PATCH 069/135] merge prop change --- Sports/.gitignore | 4 +++- Sports/tlr/merge.properties | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Sports/.gitignore b/Sports/.gitignore index 0f0ebf3..bddab10 100644 --- a/Sports/.gitignore +++ b/Sports/.gitignore @@ -4,4 +4,6 @@ # Ignore Gradle build output directory build build-output -results.xml \ No newline at end of file + +results.xml +velocity.log \ No newline at end of file diff --git a/Sports/tlr/merge.properties b/Sports/tlr/merge.properties index 3949d0b..3563f5c 100644 --- a/Sports/tlr/merge.properties +++ b/Sports/tlr/merge.properties @@ -5,7 +5,7 @@ [AppServer.Agent.Sports] numInitialSessions=2 PROPATH=${CATALINA_BASE}/webapps/Sports/WEB-INF/openedge,${CATALINA_BASE}/ablapps/Sports/openedge,${CATALINA_BASE}/openedge,${DLC}/tty,${DLC}/tty/netlib/OpenEdge.Net.apl - uuid=http://NBHYDRAHULK11:/Sports + uuid=http://${psc.as.host.name}:/Sports [AppServer.SessMgr.Sports] agentStartupParam=-T "${CATALINA_BASE}/temp" -pf ${CATALINA_BASE}/ablapps/Sports/conf/startup.pf From 96a2714a1c06e34e59f4fa10f0020695535ccce8 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 13 Sep 2023 18:33:00 +0530 Subject: [PATCH 070/135] using default OEDF plugin --- SportsApp/Sports/.dbconnection | 2 +- SportsApp/Sports/.gitignore | 3 + SportsApp/Sports/.propath | 2 +- SportsApp/Sports/build.config | 4 +- SportsApp/Sports/build.gradle | 62 +++---- SportsApp/Sports/settings.gradle | 3 +- .../Sports/tests/AppServer/CustomerTest.cls | 162 ++++++++++++++++++ .../Sports/tests/AppServer/OrderTest.cls | 43 +++++ .../tests/AppServer/SportsTestSuite.cls | 16 ++ SportsApp/Sports/tlr/merge.properties | 2 +- 10 files changed, 263 insertions(+), 36 deletions(-) create mode 100644 SportsApp/Sports/tests/AppServer/CustomerTest.cls create mode 100644 SportsApp/Sports/tests/AppServer/OrderTest.cls create mode 100644 SportsApp/Sports/tests/AppServer/SportsTestSuite.cls diff --git a/SportsApp/Sports/.dbconnection b/SportsApp/Sports/.dbconnection index 0b808fd..5c94f99 100644 --- a/SportsApp/Sports/.dbconnection +++ b/SportsApp/Sports/.dbconnection @@ -1,4 +1,4 @@ - + diff --git a/SportsApp/Sports/.gitignore b/SportsApp/Sports/.gitignore index 5a57b4e..bddab10 100644 --- a/SportsApp/Sports/.gitignore +++ b/SportsApp/Sports/.gitignore @@ -4,3 +4,6 @@ # Ignore Gradle build output directory build build-output + +results.xml +velocity.log \ No newline at end of file diff --git a/SportsApp/Sports/.propath b/SportsApp/Sports/.propath index 5ed6576..5ddc5cc 100644 --- a/SportsApp/Sports/.propath +++ b/SportsApp/Sports/.propath @@ -7,7 +7,7 @@ - + diff --git a/SportsApp/Sports/build.config b/SportsApp/Sports/build.config index aed9b5c..ad42272 100644 --- a/SportsApp/Sports/build.config +++ b/SportsApp/Sports/build.config @@ -39,7 +39,7 @@ // The directory that will contain saved .r files. If this field is blank, .r files are saved in the // same directory as the source files. // ====================================================================================================== -buildDir="build" +buildDir="${ROOT}/build" // IDE specific configurations oeide { @@ -120,7 +120,7 @@ avm { // ============================================================================================== // Boolean value to use '_progres' or 'prowin' executables. 'true' for _progres, 'false' for prowin. // ============================================================================================== - tty.enabled="false" + tty.enabled="true" // ============================================================================================== // String value to pass startup parameters. This will append to default params if diff --git a/SportsApp/Sports/build.gradle b/SportsApp/Sports/build.gradle index 620f344..9793a64 100644 --- a/SportsApp/Sports/build.gradle +++ b/SportsApp/Sports/build.gradle @@ -3,21 +3,24 @@ * * This is a general purpose Gradle build. * Learn more about Gradle by exploring our samples at https://docs.gradle.org/7.3.3/samples - * This project uses @Incubating APIs which are subject to change. */ -plugins { - id("progress.openedge.abl") version "2.2.1" - id("base") +plugins { + id "progress.openedge.abl" version "2.2.1" } + // Gather required variables -def stgEnv = System.getProperty("STAGE_ENVIRONMENT") -stgEnv = stgEnv != null ? stgEnv : System.getenv("STAGE_ENVIRONMENT") -def STAGE_ENVIRONMENT = stgEnv != null ? stgEnv : "dev" +def stageEnv = System.getProperty("STAGE_ENVIRONMENT") +stageEnv = stageEnv != null ? stageEnv : System.getenv("STAGE_ENVIRONMENT") +def STAGE_ENVIRONMENT = stageEnv != null ? stageEnv : "dev" println "DLC: ${abl.dlcHome}" println "Stage Environment: ${STAGE_ENVIRONMENT}" +group = 'com.progress.openedge' +version = "1.0.0" +description = 'dependencymodule' + abl { if (STAGE_ENVIRONMENT == "dev") { dbConnection { @@ -41,17 +44,6 @@ if (STAGE_ENVIRONMENT == "dev") { compileAbl.dependsOn "createSports2020" } -task testABLApp(type: ABLUnit){ - source("tests") - include("**/*Suite.cls") - propath("tests", "AppServer") - outputDir = "${buildDir}/test-results/test" - arguments = [haltOnFailure: "true"] -} -if (STAGE_ENVIRONMENT == "dev") { - testABLApp.dependsOn "createSports2020" -} - tasks.named("compileAbl-root-AppServer"){ rcodeDir = "${buildDir}/rcode/AppServer" } @@ -60,16 +52,29 @@ tasks.named("compileAbl-root-PASOEContent-WEB-INF-openedge"){ rcodeDir = "${buildDir}/rcode/PASOEContent/WEB-INF/openedge" } -tasks.named("compileAbl-root-tests"){ +tasks.named("compileAbl-root-tests-AppServer"){ rcodeDir = "${buildDir}/rcode/tests/AppServer" } +task testABLApp(type: ABLUnit){ + source("tests/AppServer") + include("**/*Suite.cls") + propath("tests/AppServer", "AppServer") + outputDir = "${buildDir}/test-results/test" + arguments = [haltOnFailure: "true"] +} +if (STAGE_ENVIRONMENT == "dev") { + testABLApp.dependsOn "createSports2020" +} +check.dependsOn "testABLApp" + task packageWebApp(type: OEWar){ webAppName = "Sports" + archiveVersion = "" // to ignore version in the archive name, otherwise has to be handled during deployment + verbose = true projectLocation = project.projectDir println "projectLocation: ${projectLocation.get()}" - verbose = true - destinationDirectory = project.distsDirectory + destinationDirectory = project.file "${project.distsDirectory.get()}/webapps" // exclude Sources from 'openedge' directory // (the ANT task used internally does it by default but added here anyway) @@ -88,16 +93,17 @@ task packageWebApp(type: OEWar){ } packageWebApp.dependsOn "compileAbl-root-PASOEContent-WEB-INF-openedge" -task packageABLApp(type: Oear){ +task packageABLApp(type: Oear) { ablAppName = "Sports" - destinationDirectory = project.distsDirectory //will create 'Sports.oear' file at this location - tlr { + archiveVersion = "" + destinationDirectory = project.file "${project.distsDirectory.get()}/ablapps" //will create 'Sports.oear' file at this location + tlr { from 'tlr' include '**/*.properties' include '**/*.xml' } - webapps { - from project.distsDirectory + webapps { + from "${project.distsDirectory.get()}/webapps" include '**/*.war' include '**/*.zip' } @@ -117,6 +123,4 @@ task packageABLApp(type: Oear){ } packageABLApp.dependsOn "compileAbl-root-AppServer" packageABLApp.dependsOn "packageWebApp" - -assemble.dependsOn "packageABLApp" -build.dependsOn "testABLApp" \ No newline at end of file +assemble.dependsOn "packageABLApp" \ No newline at end of file diff --git a/SportsApp/Sports/settings.gradle b/SportsApp/Sports/settings.gradle index f70f4b7..ea406c3 100644 --- a/SportsApp/Sports/settings.gradle +++ b/SportsApp/Sports/settings.gradle @@ -5,7 +5,6 @@ * * Detailed information about configuring a multi-project build in Gradle can be found * in the user manual at https://docs.gradle.org/7.3.3/userguide/multi_project_builds.html - * This project uses @Incubating APIs which are subject to change. */ -rootProject.name = 'Sports' \ No newline at end of file +rootProject.name = 'Sports' diff --git a/SportsApp/Sports/tests/AppServer/CustomerTest.cls b/SportsApp/Sports/tests/AppServer/CustomerTest.cls new file mode 100644 index 0000000..3f4a8b6 --- /dev/null +++ b/SportsApp/Sports/tests/AppServer/CustomerTest.cls @@ -0,0 +1,162 @@ + +/*------------------------------------------------------------------------ + File : CustomerTest + Purpose : + Syntax : + Description : + Author(s) : rahulk + Created : Sat Sep 09 16:43:47 IST 2023 + Notes : + ----------------------------------------------------------------------*/ + +USING Progress.Lang.*. +USING OpenEdge.Core.Assert FROM PROPATH. + +BLOCK-LEVEL ON ERROR UNDO, THROW. + +CLASS CustomerTest: + + /* DEFINE TEMP-TABLE ttOrder */ + /* FIELD Ordernum AS INTEGER LABEL "Order Num" FORMAT "zzzzzzzzz9" INITIAL "0".*/ + /* DEFINE DATASET dsCustomer1 FOR ttOrder. */ + /* + {} */ + +/* {customer.i}*/ + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @Before. + METHOD PUBLIC VOID setUpBeforeClass( ): + + RETURN. + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @Setup. + METHOD PUBLIC VOID setUp( ): + + RETURN. + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @TearDown. + METHOD PUBLIC VOID tearDown( ): + + RETURN. + + END METHOD. + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + + @After. + METHOD PUBLIC VOID tearDownAfterClass( ): + + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testReadCustomer( ): +/* DEFINE VARIABLE customerVar AS Customer NO-UNDO. */ +/* DEFINE VARIABLE iQuery AS CHARACTER NO-UNDO. */ +/* */ +/* customerVar = NEW Customer(). */ +/* iQuery="WHERE Customer.State = 'UNKNOWN'". */ +/* */ +/* DEFINE DATA-SOURCE srcCustomer FOR Customer. */ +/* /* DEFINE BUFFER OtherCust FOR Customer. */ */ +/* /* */ */ +/* /* DATASET dsCustomer:FILL(). */ */ +/* /* */ */ +/* /* DATASET dsCustomer:HANDLE. */ */ +/* */ +/* BUFFER ttCustomer:ATTACH-DATA-SOURCE(DATA-SOURCE srcCustomer:HANDLE,*/ +/* "Customer.Name,CustName"). */ +/* */ +/* customerVar:ReadCustomer(iQuery, DATASET dsCustomer). */ +/* */ +/* DATASET dsCustomer:FILL(). */ +/* BUFFER ttCustomer:DETACH-DATA-SOURCE(). */ +/* FOR EACH ttCustomer: */ +/* DISPLAY */ +/* ttCustomer.Name */ +/* ttCustomer.State. */ +/* Assert:Equals(ttCustomer.State, "hey"). */ +/* */ +/* END. */ + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testCreateCustomer( ): + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testcount( ): + DEFINE VARIABLE customerVar AS Customer NO-UNDO. + DEFINE VARIABLE iQuery AS CHARACTER NO-UNDO. + DEFINE VARIABLE oCount AS INTEGER NO-UNDO. + + customerVar = NEW Customer(). + iQuery="WHERE Customer.State = 'UNKNOWN'". + customerVar:count(iQuery, oCount). + Assert:Equals(oCount , 0). + + iQuery="WHERE Customer.State = 'NH'". + customerVar:count(iQuery, oCount). + Assert:NotEqual(oCount , 0). + Assert:IsPositive(oCount). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testDeleteCustomer( ): + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + +END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/tests/AppServer/OrderTest.cls b/SportsApp/Sports/tests/AppServer/OrderTest.cls new file mode 100644 index 0000000..ff81736 --- /dev/null +++ b/SportsApp/Sports/tests/AppServer/OrderTest.cls @@ -0,0 +1,43 @@ + +/*------------------------------------------------------------------------ + File : OrderTest + Purpose : + Syntax : + Description : + Author(s) : rahulk + Created : Sat Sep 09 19:40:47 IST 2023 + Notes : + ----------------------------------------------------------------------*/ + +USING Progress.Lang.*. +USING OpenEdge.Core.Assert FROM PROPATH. + +BLOCK-LEVEL ON ERROR UNDO, THROW. + +CLASS OrderTest: + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testCreateOrder( ): + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + + + /*------------------------------------------------------------------------------ + Purpose: + Notes: + ------------------------------------------------------------------------------*/ + @Test. + METHOD PUBLIC VOID testReadOrder( ): + Assert:IsTrue(TRUE). + RETURN. + + END METHOD. + +END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/tests/AppServer/SportsTestSuite.cls b/SportsApp/Sports/tests/AppServer/SportsTestSuite.cls new file mode 100644 index 0000000..516839c --- /dev/null +++ b/SportsApp/Sports/tests/AppServer/SportsTestSuite.cls @@ -0,0 +1,16 @@ + + /*------------------------------------------------------------------------ + File : SportsTestSuite + Syntax : + Author(s) : rahulk + Created : Sat Sep 09 16:41:21 IST 2023 + Notes : + ----------------------------------------------------------------------*/ + +USING Progress.Lang.*. +BLOCK-LEVEL ON ERROR UNDO, THROW. + +@TestSuite(classes= "CustomerTest, OrderTest"). +CLASS SportsTestSuite: + +END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/tlr/merge.properties b/SportsApp/Sports/tlr/merge.properties index 3949d0b..3563f5c 100644 --- a/SportsApp/Sports/tlr/merge.properties +++ b/SportsApp/Sports/tlr/merge.properties @@ -5,7 +5,7 @@ [AppServer.Agent.Sports] numInitialSessions=2 PROPATH=${CATALINA_BASE}/webapps/Sports/WEB-INF/openedge,${CATALINA_BASE}/ablapps/Sports/openedge,${CATALINA_BASE}/openedge,${DLC}/tty,${DLC}/tty/netlib/OpenEdge.Net.apl - uuid=http://NBHYDRAHULK11:/Sports + uuid=http://${psc.as.host.name}:/Sports [AppServer.SessMgr.Sports] agentStartupParam=-T "${CATALINA_BASE}/temp" -pf ${CATALINA_BASE}/ablapps/Sports/conf/startup.pf From 5ee0fc838f790d3244d48d67474a1e56f3c00c0e Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 13 Sep 2023 19:48:02 +0530 Subject: [PATCH 071/135] docker build for sports app --- SportsApp/Sports/Dockerfile | 17 +++++++++++++++++ SportsApp/Sports/build.gradle | 20 ++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 SportsApp/Sports/Dockerfile diff --git a/SportsApp/Sports/Dockerfile b/SportsApp/Sports/Dockerfile new file mode 100644 index 0000000..c2410da --- /dev/null +++ b/SportsApp/Sports/Dockerfile @@ -0,0 +1,17 @@ +FROM alpine:3.8 + +# Root location +ARG ROOT_FOLDER=/deploy-staging + +ARG MANIFEST_VERSION=1.0 + +# Copy archive file +COPY ./ablapps/ ${ROOT_FOLDER}/artifacts/ablapps + +# Create "META-INF/MANIFEST.MF" file +RUN mkdir ${ROOT_FOLDER}/META-INF +RUN echo "Manifest-Version: ${MANIFEST_VERSION}" > ${ROOT_FOLDER}/META-INF/MANIFEST.MF +RUN echo "Date-Timestamp: `date +'%Y-%m-%dT%H:%M:%S%z'`" >> ${ROOT_FOLDER}/META-INF/MANIFEST.MF + +# Set working directory +WORKDIR ${ROOT_FOLDER} diff --git a/SportsApp/Sports/build.gradle b/SportsApp/Sports/build.gradle index 9793a64..ef55b21 100644 --- a/SportsApp/Sports/build.gradle +++ b/SportsApp/Sports/build.gradle @@ -19,7 +19,7 @@ println "Stage Environment: ${STAGE_ENVIRONMENT}" group = 'com.progress.openedge' version = "1.0.0" -description = 'dependencymodule' +description = 'Sports App' abl { if (STAGE_ENVIRONMENT == "dev") { @@ -123,4 +123,20 @@ task packageABLApp(type: Oear) { } packageABLApp.dependsOn "compileAbl-root-AppServer" packageABLApp.dependsOn "packageWebApp" -assemble.dependsOn "packageABLApp" \ No newline at end of file +assemble.dependsOn "packageABLApp" + +task dockerBuild(type:Exec) { + workingDir "${buildDir}/docker" + commandLine 'bash', '-c', "docker build --no-cache -t ${project.name.toLowerCase()}:latest ." + doFirst { + copy { + from "${project.distsDirectory.get()}/ablapps/" + into "${buildDir}/docker/ablapps" + } + copy { + from "Dockerfile" + into "${buildDir}/docker" + } + } +} +dockerBuild.dependsOn build \ No newline at end of file From 38a7b0d3a2950d5b96aba4e3557fbc5446313419 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 13 Sep 2023 20:25:38 +0530 Subject: [PATCH 072/135] docker image for webui app --- webui/Dockerfile | 17 + webui/src/grid.js | 76 + webui/src/index.html | 63 + webui/src/progress.all.js | 14881 ++++++++++++++++++++++++++++++++++++ 4 files changed, 15037 insertions(+) create mode 100644 webui/Dockerfile create mode 100644 webui/src/grid.js create mode 100644 webui/src/index.html create mode 100644 webui/src/progress.all.js diff --git a/webui/Dockerfile b/webui/Dockerfile new file mode 100644 index 0000000..5069b7b --- /dev/null +++ b/webui/Dockerfile @@ -0,0 +1,17 @@ +FROM alpine:3.8 + +# Root location +ARG ROOT_FOLDER=/webui +ARG MANIFEST_VERSION=1.0 + +# Create "META-INF/MANIFEST.MF" file +RUN mkdir -p ${ROOT_FOLDER}/META-INF +RUN echo "Manifest-Version: ${MANIFEST_VERSION}" > ${ROOT_FOLDER}/META-INF/MANIFEST.MF +RUN echo "Date-Timestamp: `date +'%Y-%m-%dT%H:%M:%S%z'`" >> ${ROOT_FOLDER}/META-INF/MANIFEST.MF + +# Copy web app files +COPY ./src/ ${ROOT_FOLDER}/ + +# Set working directory +WORKDIR ${ROOT_FOLDER} + diff --git a/webui/src/grid.js b/webui/src/grid.js new file mode 100644 index 0000000..e6c9df0 --- /dev/null +++ b/webui/src/grid.js @@ -0,0 +1,76 @@ +/*global alert, $, progress, require */ + +// require("@progress/jsdo"); + +$(function () { + 'use strict'; + var serviceURI = "https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports"; + var catalogURI = serviceURI + "/static/SportsService.json"; + + function createGrid() { + console.log("DEBUG: createGrid(): "); + $('#grid').kendoGrid({ + dataSource: { + serverFiltering: false, + serverSorting: false, + serverPaging: false, + type: "jsdo", + transport: { + jsdo: "Customer" + }, + error: function (e) { + var messages = ""; + if (e.errorThrown) { + messages += e.errorThrown.message + "\n"; + } + e.sender.transport.jsdo.getErrors().forEach(function (err) { + messages += err.error + "\n"; + }); + alert("Error: \n" + messages); + } + }, + selectable: "multiple row", + navigatable: true, + filterable: true, + height: 400, + groupable: true, + reorderable: true, + resizable: true, + sortable: true, + pageable: { + refresh: true, + pageSizes: true, + pageSize: 10, + buttonCount: 5 + }, + editable: 'inline', + toolbar: ['create'], + columns: [ + { + field: 'CustNum', + title: 'Cust Num', + width: 100 + }, + {field: 'Name'}, + {field: 'State'}, + {field: 'Country'}, + {command: ['edit', 'destroy'], title: ' ', width: '250px'} + ] + }); + } + + try { + // Create a new session object + progress.data.getSession({ + serviceURI: serviceURI, + catalogURI: catalogURI, + authenticationModel: "anonymous" + }).then(function (/* jsdosession, result, info */) { + createGrid(); + }, function (/* jsdosession, result, info */) { + alert("Error while creating session."); + }); + } catch (e) { + alert("Error instantiating objects: " + e); + } +}); diff --git a/webui/src/index.html b/webui/src/index.html new file mode 100644 index 0000000..7eee979 --- /dev/null +++ b/webui/src/index.html @@ -0,0 +1,63 @@ + + + + + PUG Challenge 2023! + + + + + + + + + + + + + + +

+

Welcome to PUG Challenge 2023!

+
+ +
+

SportsApp

+

Customer Management

+
+
+ +
+
+

Powered by © Progress Software Corporation

+
+
+ + + \ No newline at end of file diff --git a/webui/src/progress.all.js b/webui/src/progress.all.js new file mode 100644 index 0000000..5082570 --- /dev/null +++ b/webui/src/progress.all.js @@ -0,0 +1,14881 @@ +/* +Progress JSDO Version: 4.4.1 + +Copyright 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. +*/ +/* +progress.util.js Version: 4.4.0-7 + +Copyright (c) 2014-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +Contains support objects used by the jsdo and/or session object + +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. + + */ +/*global progress:true*/ +/*jslint nomen: true*/ +(function () { + + /* Define these if not defined yet - they may already be defined if + * progress.js was included first */ + if (typeof progress === "undefined") { + progress = {}; + } + + if (typeof progress.data === "undefined") { + progress.data = {}; + } + + progress.util = {}; + + var STRING_OBJECT_TYPE = "String", + DATE_OBJECT_TYPE = "Date", + CHARACTER_ABL_TYPE = "CHARACTER"; + + + /** + * Utility class that allows subscribing and unsubscribing from named events. + * + * @returns {progress.util.Observable} + */ + progress.util.Observable = function () { + /* + * Example format of the events object. Some event delegates may only + * have a function setup, others may optionally have scope, and possibly an operation filter + * + * var events = { + * afterfill : [{ + * scope : {}, // this is optional + * fn : function () {}, + * operation : 'getCustomers' // this is optional + * }, ...] + * + * } + * + * + * + */ + + /* + * remove the given function from the array of observers + */ + function _filterObservers(observers, fn, scope, operation) { + return observers.filter(function (el) { + if (el.fn !== fn || el.scope !== scope || el.operation !== operation) { + return el; + } + }, this); + } + + /* + * validate the arguments passed to the subscribe function + */ + this.validateSubscribe = function (args, evt, listenerData) { + + if (args.length >= 2 && (typeof args[0] === 'string') && (typeof args[1] === 'string')) { + listenerData.operation = args[1]; + listenerData.fn = args[2]; + listenerData.scope = args[3]; + + } else if (args.length >= 2 && (typeof args[0] === 'string') && (typeof args[1] === 'function')) { + listenerData.operation = undefined; + listenerData.scope = args[2]; + listenerData.fn = args[1]; + } else { + throw new Error(); + } + + }; + + + /* + * bind the specified function so it receives callbacks when the + * specified event name is called. Event name is not case sensitive. + * An optional scope can be provided so that the function is executed + * in the given scope. If no scope is given, then the function will be + * called without scope. + * + * If the same function is registered for the same event a second time with + * the same scope the original subscription is removed and replaced with the new function + * to be called in the new scope. + * + * This method has two signatures. + * + * Signature 1: + * @param evt The name of the event to bind a handler to. String. Not case sensitive. + * @param fn The function callback for the event . Function. + * @param scope The scope the function is to be run in. Object. Optional. + * + * Signature 2: + * + * @param evt The name of the event to bind a handler to. String. Not case sensitive + * @param operation The name of the operation to bind to. String. Case sensitive. + * @param fn The function callback for the event . Function. + * @param scope The scope the function is to be run in. Object. Optional. + + */ + this.subscribe = function (evt, operation, fn, scope) { + var listenerData, + observers; + + if (!evt) { + throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "subscribe")); + } + + if (typeof evt !== 'string') { + throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), + "subscribe", progress.data._getMsgText("jsdoMSG039"))); + } + + this._events = this._events || {}; + evt = evt.toLowerCase(); + listenerData = {fn: undefined, scope: undefined, operation: undefined}; + + try { + this.validateSubscribe(arguments, evt, listenerData); + } catch (e) { + throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), + "subscribe", e.message)); + } + + observers = this._events[evt] || []; + + // make sure we don't add duplicates + observers = _filterObservers(observers, listenerData.fn, + listenerData.scope, listenerData.operation); + observers.push(listenerData); + this._events[evt] = observers; + + return this; + }; + + /* + * remove the specified function so it no longer receives events from + * the given name. event name is not case sensitive. + * + * This method has two signaturues. + * Signature 1: + * @param evt Required. The name of the event for which to unbind the given function. String. + * @param fn Required. The function to remove from the named event. Function. + * @param scope Optional. The function scope in which to remove the listener. Object. + * + * Signature 2: + * + * @param evt Required. The name of the event for which to unbind the given function. + String. Not case sensitive + * @param operation Required. The name of the operation to receive events. String. Case Sensitive + * @param fn Required. The function to remove from the named event. Function. + * @param scope Optional. The function scope in which to remove the listener. Object. + * + */ + this.unsubscribe = function (evt, operation, fn, scope) { + var listenerData, + observers; + + if (!evt) { + throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "unsubscribe")); + } + + if (typeof evt !== 'string') { + throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), + "unsubscribe", progress.data._getMsgText("jsdoMSG037"))); + } + + this._events = this._events || {}; + evt = evt.toLowerCase(); + listenerData = {fn: undefined, scope: undefined, operation: undefined}; + try { + this.validateSubscribe(arguments, evt, listenerData); + } catch (e) { + // throw new Error("Invalid signature for unsubscribe. " + e.message); + throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), + "unsubscribe", e.message)); + } + + observers = this._events[evt] || []; + if (observers.length > 0) { + this._events[evt] = _filterObservers(observers, listenerData.fn, + listenerData.scope, listenerData.operation); + } + + return this; + }; + + /* + * trigger an event of the given name, and pass the specified data to + * the subscribers of the event. Event name is not case sensitive. + * A variable numbers of arguments can be passed as arguments to the event handler. + * + * This method has two signatures + * Signature 1: + * @param evt The name of the event to fire. String. Not case sensitive. + * @param operation The name of the operation. String. Case sensitive + * @param args Optional. A variable number of arguments to pass to the event handlers. + * + * Signature 2: + * @param evt The name of the event to fire. String. Not case sensitive + * @param args Optional. A variable number of arguments to pass to the event handlers. + */ + this.trigger = function (evt, operation, args) { + var observers, + op; + + if (!evt) { + throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "trigger")); + } + + this._events = this._events || {}; + evt = evt.toLowerCase(); + observers = this._events[evt] || []; + if (observers.length > 0) { + args = Array.prototype.slice.call(arguments); + + if ((arguments.length >= 2) + && (typeof evt === 'string') + && (typeof operation === 'string')) { + // in alt format the second argument is the event name, + // and the first is the operation name + op = operation; + args = args.length > 2 ? args.slice(2) : []; + } else if (arguments.length >= 1 && (typeof evt === 'string')) { + op = undefined; + args = args.length > 1 ? args.slice(1) : []; + } else { + throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), "trigger")); + } + + observers.forEach(function (el) { + if (el.operation === op) { + el.fn.apply(el.scope, args); + } + }); + + } + + return this; + }; + + // unbind all listeners from the given event. If the + // evt is undefined, then all listeners for all events are unbound + // evnt name is not case sensitive + // @param evt Optional. The name of the event to unbind. If not passed, then all events are unbound + this.unsubscribeAll = function (evt, operation) { + var observers; + + if (evt) { + this._events = this._events || {}; + if (typeof evt === 'string') { + evt = evt.toLowerCase(); + observers = this._events[evt] || []; + + observers.forEach(function (el) { + if (el.operation) { + this.unsubscribe(evt, el.operation, el.fn, el.scope); + } else { + this.unsubscribe(evt, el.fn, el.scope); + } + }, this); + } + } else { + this._events = {}; + } + + return this; + }; + }; + + + /** + * Utility class that saves/reads data to localStorage + * + * @returns {progress.data.LocalStorage} + */ + progress.data.LocalStorage = function LocalStorage() { + + /*global localStorage */ + if (typeof localStorage === "undefined") { + // "progress.data.LocalStorage: No support for localStorage." + throw new Error(progress.data._getMsgText("jsdoMSG126", "progress.data.LocalStorage", "localStorage")); + } + + + // "Methods" + + this.saveToLocalStorage = function (name, dataObj) { + localStorage.setItem(name, JSON.stringify(dataObj)); + }; + + this.readFromLocalStorage = function (name) { + + var jsonStr = localStorage.getItem(name), + dataObj = null; + + if (jsonStr !== null) { + try { + dataObj = JSON.parse(jsonStr); + } catch (e) { + dataObj = null; + } + } + return dataObj; + }; + + this.clearLocalStorage = function (name) { + localStorage.removeItem(name); + }; + + }; // End of LocalStorage + + + ///////////////////////////////////////////////////////////////////////////////////////// + // Utility Functions + + /* + * Converts the specified filter object to an OpenEdge ABL Where String. + * + * @param tableRef - handle to the table in jsdo, where string is applied to. + * @param filter - the filter object to convert. + * + * @returns - translated OE where string. + */ + progress.util._convertToABLWhereString = function (tableRef, filter) { + var result = [], + logic = filter.logic || "and", + idx, + length, + field, + fieldInfo, + type, + format, + operator, + value, + ablType, + //filters = (filter.filters) ? filter.filters : [filter], + filters = filter.filters || [filter], + + whereOperators = { + eq: "=", + neq: "<>", + gt: ">", + gte: ">=", + lt: "<", + lte: "<=", + contains : "INDEX", + doesnotcontain: "INDEX", + endswith: "R-INDEX", + startswith: "BEGINS", + isnull: "ISNULL", + isnotnull: "ISNOTNULL", + isempty: "ISEMPTY", + isnotempty: "ISNOTEMPTY" + }; + + for (idx = 0, length = filters.length; idx < length; idx += 1) { + filter = filters[idx]; + field = filter.field; + value = filter.value; + + if (filter.filters) { + filter = progress.util._convertToABLWhereString(tableRef, filter); + } else { + // Use original field name instead of serialized name + if (field && tableRef._name) { + fieldInfo = tableRef._jsdo[tableRef._name]._fields[field.toLowerCase()]; + if (fieldInfo && fieldInfo.origName) { + field = fieldInfo.origName; + } + } + + operator = whereOperators[filter.operator]; + + if (operator === undefined) { + throw new Error("The operator " + filter.operator + " is not valid."); + } + + switch (filter.operator) { + case "isnull": + case "isnotnull": + case "isempty": + case "isnotempty": + value = undefined; + break; + } + + if (operator && value !== undefined) { + type = progress.util._getObjectType(value); + + // We need to build a template format string for the where string. + // We'll first add positional info for the value + if (type === STRING_OBJECT_TYPE) { + format = "'{1}'"; + value = value.replace(/'/g, "~'"); + } else if (type === DATE_OBJECT_TYPE) { + ablType = tableRef._getABLType(field); + if (ablType === "DATE") { + format = "DATE({1:MM, dd, yyyy})"; + } else if (ablType === "DATETIME-TZ") { + // zzz here means to translate timezone offset into minutes + format = "DATETIME-TZ({1:MM, dd, yyyy, hh, mm, ss, fff, zzz})"; + } else { + format = "DATETIME({1:MM, dd, yyyy, hh, mm, ss, fff})"; + } + } else { + format = "{1}"; + } + + // Most where strings are in the format: field operator value. Ex. custnum < 100 + // An exception to this is INDEX() and R-INDEX() which have format: operator field value + // Ex. R-INDEX(name, "LTD") + if (operator === "INDEX" || operator === "R-INDEX") { + if (type !== STRING_OBJECT_TYPE) { + throw new Error("Error parsing filter object. The operator " + filter.operator + + " requires a string value"); + } + if (filter.operator === "doesnotcontain") { + format = "{0}(" + "{2}, " + format + ") = 0"; + } else if (filter.operator === "contains") { + format = "{0}(" + "{2}, " + format + ") > 0"; + } else { // else filter.operator = "endswith" + format = "{2} MATCHES '*{1}'"; + } + } else { + format = "{2} {0} " + format; + } + + filter = progress.util._format(format, operator, value, field); + } else if (operator && value === undefined) { + if (filter.operator === "isempty" || filter.operator === "isnotempty") { + ablType = tableRef._getABLType(field); + if (ablType !== CHARACTER_ABL_TYPE) { + throw new Error("Error parsing filter object. The operator " + filter.operator + + " requires a CHARACTER field"); + } + if (filter.operator === "isempty") { + format = "{2} = ''"; + } else if (filter.operator === "isnotempty") { + format = "{2} <> ''"; + } + } else { + if (filter.operator === "isnull") { + format = "{2} = ?"; + } else if (filter.operator === "isnotnull") { + format = "{2} <> ?"; + } else { + format = "{2} {0} ?"; + } + } + + // format, operator {0}, value {1}, field {2} + filter = progress.util._format(format, operator, value, field); + } + } + + result.push(filter); + } + + filter = result.join(" " + logic + " "); + + if (result.length > 1) { + filter = "(" + filter + ")"; + } + + return filter; + }; + + + /* + * Converts the specified filter object to an SQL Query String. + * + * @param tableName - tableName of table in jsdo, where clause is applied to. + * @param filter - the filter object to convert. + * + * @returns - translated SQL where clause. + */ + progress.util._convertToSQLQueryString = function (tableRef, filter, addSelect) { + var result = [], + logic = filter.logic || "and", + idx, + length, + field, + type, + format, + operator, + value, + fieldFormat, + filters = filter.filters || [filter], + filterStr, + usingLike = true, + + whereOperators = { + eq: "=", + neq: "!=", + gt: ">", + gte: ">=", + lt: "<", + lte: "<=", + contains : "LIKE", + doesnotcontain: "NOT LIKE", + endswith: "LIKE", + startswith: "LIKE", + isnull: "ISNULL", + isnotnull: "ISNOTNULL", + isempty: "ISEMPTY", + isnotempty: "ISNOTEMPTY" + }; + + if (typeof addSelect === "undefined") { + addSelect = false; + } + + for (idx = 0, length = filters.length; idx < length; idx += 1) { + filter = filters[idx]; + field = filter.field; + value = filter.value; + + if (filter.filters) { + filterStr = progress.util._convertToSQLQueryString(tableRef, filter, false); + } else { + operator = whereOperators[filter.operator]; + + if (operator === undefined) { + throw new Error("The operator " + filter.operator + " is not valid."); + } + + switch (filter.operator) { + case "isnull": + case "isnotnull": + case "isempty": + case "isnotempty": + value = undefined; + break; + } + + if (operator && value !== undefined) { + type = progress.util._getObjectType(value); + + if (operator === "LIKE" || operator === "NOT LIKE") { + if (type !== STRING_OBJECT_TYPE) { + throw new Error("Error parsing filter object. The operator " + filter.operator + + " requires a string value"); + } + } + + if (type === STRING_OBJECT_TYPE) { + format = "'{1}'"; + value = value.replace(/'/g, "''"); + } else if (type === DATE_OBJECT_TYPE) { + fieldFormat = tableRef._getFormat(field); + if (fieldFormat === "date") { + format = "'{1:yyyy-MM-dd}'"; + } else if (fieldFormat === "date-time") { + format = "{1:#ISO(iso)}"; + } else if (fieldFormat === "time") { + format = "'{1:FFF}'"; + } + } else { + format = "{1}"; + } + + // We need to build a template format string for the where string. + // We'll first add positional info for the value, which is represented by {1} + if (filter.operator === "startswith") { + format = "'{1}%'"; + } else if (filter.operator === "endswith") { + format = "'%{1}'"; + } else if (filter.operator === "contains" || filter.operator === "doesnotcontain") { + format = "'%{1}%'"; + } else { + usingLike = false; + } + + if (usingLike) { + value = value.replace(/%/g, '\\%'); + value = value.replace(/_/g, '\\_'); + } + + format = "{2} {0} " + format; + filterStr = progress.util._format(format, operator, value, field); + } else if (operator && value === undefined) { + if (filter.operator === "isempty" || filter.operator === "isnotempty") { + type = tableRef._fields[field.toLowerCase()].type; + if (type !== STRING_OBJECT_TYPE.toLowerCase()) { + throw new Error("Error parsing filter object. The operator " + filter.operator + + " requires a string field"); + } + if (filter.operator === "isempty") { + format = "{2} = ''"; + } else if (filter.operator === "isnotempty") { + format = "{2} != ''"; + } + } else { + if (filter.operator === "isnull") { + format = "{2} IS NULL"; + } else if (filter.operator === "isnotnull") { + format = "{2} IS NOT NULL"; + } else { + format = "{2} {0} NULL"; + } + } + + // format, operator {0}, value {1}, field {2} + filterStr = progress.util._format(format, operator, value, field); + } + } + + result.push(filterStr); + } + + filterStr = result.join(" " + logic + " "); + + if (result.length > 1) { + filterStr = "(" + filterStr + ")"; + } + + if (addSelect === true) { + filterStr = "SELECT * FROM " + tableRef._name + " WHERE " + filterStr; + } + + return filterStr; + }; + + + /* + * Returns the object type; Example "String", "Date" + * Constants for object type values are defined above. + * + * @param value - the object whose type is returned + */ + progress.util._getObjectType = function (value) { + // Returns [object xxx]. Removing [object ] + return Object.prototype.toString.call(value).slice(8, -1); + }; + + + /* + * Substitutes in a variable number of arguments into specified format string (with place-holders) + * + * @param fmt - the format string with place-holders, eg. "{0} text {1}". + * + * @returns - formatted string. + */ + progress.util._format = function (fmt) { + /*jslint regexp: true*/ + var values = arguments, + formatRegExp = /\{(\d+)(:[^\}]+)?\}/g; + /*jslint regexp: false*/ + + return fmt.replace(formatRegExp, function (match, index, placeholderFormat) { + var value = values[parseInt(index, 10) + 1]; + + return progress.util._toString(value, placeholderFormat ? placeholderFormat.substring(1) : ""); + }); + + }; + + /* + * Converts the specified value param to a string. + * + * @param value - object to convert + * @param fmt - optional format string with place-holders, eg. "MM dd yyyy". + * + * @returns - converted string. + */ + progress.util._toString = function (value, fmt) { + var str; + + if (fmt) { + if (progress.util._getObjectType(value) === "Date") { + return progress.util._formatDate(value, fmt); + } + } + + if (typeof value === "number") { + str = value.toString(); + } else { + str = (value !== undefined ? value : ""); + } + + return str; + }; + + /* + * Accepts string representing number and optionally pads it with "0"'s to conform to + * specified number of digits. + * + * @param number - string representing number to pad. + * @param digit - number of digits desired for padded string. If not specified, default is 2. + * + * @returns - padded string representing number. + */ + progress.util._pad = function (number, digits) { + var zeros = ["", "0", "00", "000", "0000"], + end; + + number = String(number); + digits = digits || 2; + end = digits - number.length; + + if (end) { + return zeros[digits].substring(0, end) + number; + } + return number; + }; + + /* + * Converts the specified date param to a string. + * + * @param date - date object to convert + * @param fmt - format string with place-holders, eg. "MM dd yyyy". + * + * @returns - converted string. + */ + progress.util._formatDate = function (date, format) { + /*jslint regexp: true*/ + var dateFormatRegExp = + /dd|MM|yyyy|hh|mm|fff|FFF|ss|zzz|iso|"[^"]*"|'[^']*'/g; + /*jslint regexp: false*/ + + return format.replace(dateFormatRegExp, function (match) { + var minutes, + result, + sign; + + if (match === "dd") { + result = progress.util._pad(date.getDate()); + } else if (match === "MM") { + result = progress.util._pad(date.getMonth() + 1); + } else if (match === "yyyy") { + result = progress.util._pad(date.getFullYear(), 4); + } else if (match === "hh") { + result = progress.util._pad(date.getHours()); + } else if (match === "mm") { + result = progress.util._pad(date.getMinutes()); + } else if (match === "ss") { + result = progress.util._pad(date.getSeconds()); + } else if (match === "fff") { + result = progress.util._pad(date.getMilliseconds(), 3); + } else if (match === "FFF") { + result = String(date.getTime()); + } else if (match === "zzz") { + // timezone is returned in minutes + minutes = date.getTimezoneOffset(); + sign = minutes < 0; + result = (sign ? "+" : "-") + minutes; + } else if (match === "iso") { + result = date.toISOString(); + } + + return result !== undefined ? result : match.slice(1, match.length - 1); + }); + }; + + /* + * Processes settings in a jsdoSettings object. + * This method is used by project templates. + */ + progress.util.jsdoSettingsProcessor = function jsdoSettingsProcessor(jsdoSettings) { + if (typeof jsdoSettings === 'object') { + if (jsdoSettings.authenticationModel === undefined || jsdoSettings.authenticationModel === "") { + jsdoSettings.authenticationModel = "ANONYMOUS"; + } + } + }; + +}()); +//# sourceURL=progress.jsdo.js +/* +progress.js Version: 4.4.1-01 + +Copyright (c) 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + // "use strict"; + + var PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS = 20, + PROGRESS_JSDO_OP_STRING = ["none", "create", "read", "update", "delete", "submit"], + PROGRESS_JSDO_ROW_STATE_STRING = ["", "created", "", "modified", "deleted"]; + + /* define these if not defined yet - they may already be defined if + progress.session.js was included first */ + if (typeof progress === 'undefined') { + progress = {}; + } + if (typeof progress.data === 'undefined') { + progress.data = {}; + } + + progress.data._nextid = 0; + progress.data._uidprefix = "" + ( Date.now ? Date.now() : (new Date().getTime())); + + /* 15 - 9 */ + var UID_MAX_VALUE = 999999999999999; + + progress.data._getNextId = function () { + var uid = ++progress.data._nextid; + if (uid >= UID_MAX_VALUE) { + progress.data._nextid = uid = 1; + progress.data._uidprefix = "" + ( Date.now ? Date.now() : (new Date().getTime())); + } + + return progress.data._uidprefix + "-" + uid; + }; + + + var msg = {}; + msg.msgs = {}; +// msg numbers 0 - 99 are related to use of the API (methods and properties we expose to developers) +// 100 - 109 relate to network errors +// 110 - 998 are for miscellaneous errors + + msg.msgs.jsdoMSG000 = "JSDO, Internal Error: {1}"; + msg.msgs.jsdoMSG001 = "JSDO: JSDO has multiple tables. Please use {1} at the table reference level."; + msg.msgs.jsdoMSG002 = "JSDO: Working record for '{1}' is undefined."; + msg.msgs.jsdoMSG003 = "JSDO: {1} function requires a function as a parameter."; + msg.msgs.jsdoMSG004 = "JSDO: Unable to find resource '{1}' in the catalog."; + msg.msgs.jsdoMSG005 = "JSDO: Data for table '{1}' was not specified in addRecords() call."; + msg.msgs.jsdoMSG006 = "JSDO: Data for JSDO was not specified in addRecords() call."; + msg.msgs.jsdoMSG007 = "JSDO: Test function in {1} must return a boolean."; + msg.msgs.jsdoMSG008 = "JSDO: Invalid keyFields parameter in addRecords() call."; + msg.msgs.jsdoMSG009 = "JSDO: KeyField '{1}' in addRecords() call was not found in the schema."; + msg.msgs.jsdoMSG010 = "JSDO: Field '{1}' in relationship was not found in the schema."; + msg.msgs.jsdoMSG011 = "UIHelper: JSDO has multiple tables. " + + "Please use {1} at the table reference level."; + msg.msgs.jsdoMSG012 = "UIHelper: Invalid {2} parameter in {1} call."; + msg.msgs.jsdoMSG020 = "JSDO: tableName parameter must be a string in addRecords() call."; + msg.msgs.jsdoMSG021 = "JSDO: addMode parameter must be specified in addRecords() call."; + msg.msgs.jsdoMSG022 = "JSDO: Invalid addMode specified in addRecords() call."; + msg.msgs.jsdoMSG023 = "JSDO: Duplicate found in addRecords() call using APPEND mode."; + msg.msgs.jsdoMSG024 = "{1}: Unexpected signature in call to {2} function."; + msg.msgs.jsdoMSG025 = "{1}: Invalid parameters in call to {2} function."; + msg.msgs.jsdoMSG026 = "JSDO: saveChanges requires a " + + "CREATE, UPDATE, DELETE or SUBMIT operation to be defined."; + msg.msgs.jsdoMSG030 = "JSDO: Invalid {1}, expected {2}."; + msg.msgs.jsdoMSG031 = "JSDO: Specified sort field name '{1}' was not found in the schema."; + msg.msgs.jsdoMSG032 = "JSDO: Before-image data already exists for record in addRecords() call."; + msg.msgs.jsdoMSG033 = "{1}: Invalid signature for {2}. {3}"; + msg.msgs.jsdoMSG034 = "JSDO: In '{1}' function, JSON data is missing _id"; + msg.msgs.jsdoMSG035 = "JSDO: In '{1}' function, before-image JSON data is missing prods:clientId"; + msg.msgs.jsdoMSG036 = "JSDO: '{1}' can only be called for a dataset"; + msg.msgs.jsdoMSG037 = "{1}: Event name must be provided for {2}."; + msg.msgs.jsdoMSG038 = "Too few arguments. There must be at least {1}."; + msg.msgs.jsdoMSG039 = "The name of the event is not a string."; + msg.msgs.jsdoMSG040 = "The event listener is not a function."; + msg.msgs.jsdoMSG041 = "The event listener scope is not an object."; + msg.msgs.jsdoMSG042 = "'{1}' is not a defined event for this object."; + msg.msgs.jsdoMSG043 = "{1}: A session object was requested to check the status of a Mobile " + + "Service named '{2}', but it has not loaded the definition of that service."; + msg.msgs.jsdoMSG044 = "JSDO: In '{1}' function, {2} is missing {3} property."; + msg.msgs.jsdoMSG045 = "JSDO: {1} function: {2} is missing {3} property."; + msg.msgs.jsdoMSG046 = "JSDO: {1} operation is not defined."; + msg.msgs.jsdoMSG047 = "{1} timeout expired."; + msg.msgs.jsdoMSG048 = "{1}: {2} method has argument '{3}' that is missing property '{4}'."; + msg.msgs.jsdoMSG049 = "{1}: Unexpected error calling {2}: {3}"; + msg.msgs.jsdoMSG050 = "No token returned from server"; + msg.msgs.jsdoMSG051 = "{1} The login method was not executed because the AuthenticationProvider is already logged in."; + msg.msgs.jsdoMSG052 = "{1}: The login method was not executed because no credentials were supplied."; + msg.msgs.jsdoMSG053 = "{1}: {2} was not executed because the AuthenticationProvider is not logged in."; + msg.msgs.jsdoMSG054 = "{1}: Token refresh was not executed because the AuthenticationProvider does not have a refresh token."; + msg.msgs.jsdoMSG055 = "{1}: Token refresh was not executed because the authentication model is not sso."; + msg.msgs.jsdoMSG056 = "{1}: Already logged in."; + msg.msgs.jsdoMSG057 = "{1}: Cannot call {2} when authenticationModel is SSO. Please use the AuthenticationProvider object instead."; + msg.msgs.jsdoMSG058 = "{1}: Cannot pass username and password to addCatalog when authenticationModel " + + "is sso. Pass an AuthenticationProvider instead."; + msg.msgs.jsdoMSG059 = "{1}: Error in constructor. The authenticationModels of the " + + "AuthenticationProvider ({2}) and the JSDOSession ({3}) were not compatible."; + msg.msgs.jsdoMSG060 = "AuthenticationProvider: AuthenticationProvider is no longer logged in. " + + "Tried to refresh SSO token but failed due to authentication error at token server."; + msg.msgs.jsdoMSG061 = "{1}: Attempted to set {2} property to an invalid value."; + + // 100 - 109 relate to network errors + msg.msgs.jsdoMSG100 = "JSDO: Unexpected HTTP response. Too many records."; + msg.msgs.jsdoMSG101 = "Network error while executing HTTP request."; + + // 110 - 499 are for miscellaneous errors + msg.msgs.jsdoMSG110 = "Catalog error: idProperty not specified for resource '{1}'. " + + "idProperty is required {2}."; + msg.msgs.jsdoMSG111 = "Catalog error: Schema '{1}' was not found in catalog."; + msg.msgs.jsdoMSG112 = "Catalog error: Output parameter '{1}' was not found for operation '{2}'."; + msg.msgs.jsdoMSG113 = "Catalog error: Found xType '{1}' for output parameter '{2}' " + + "for operation '{3}' but xType DATASET, TABLE or ARRAY was expected."; + msg.msgs.jsdoMSG114 = "JSDO: idProperty '{1}' is missing from '{2}' record."; + msg.msgs.jsdoMSG115 = "JSDO: Invalid option specified in {1}() call."; + msg.msgs.jsdoMSG116 = "JSDO: {1} parameter must be a string in {2} call."; + msg.msgs.jsdoMSG117 = "JSDO: Schema from storage area '{1}' does not match JSDO schema"; + msg.msgs.jsdoMSG118 = "JSDO: Plugin '{1}' was not found."; + msg.msgs.jsdoMSG119 = "JSDO: A mappingType is expected when 'capabilities' is set." + + " Please specify a plugin (ex: JFP)."; + msg.msgs.jsdoMSG120 = "JSDO: Parameter '{2}' requires capability '{1}' in the catalog."; + msg.msgs.jsdoMSG121 = "{1}: Argument {2} must be of type {3} in {4} call."; + msg.msgs.jsdoMSG122 = "{1}: Incorrect number of arguments in {2} call. There should be {3}."; + msg.msgs.jsdoMSG123 = "{1}: A server response included an invalid '{2}' header."; + msg.msgs.jsdoMSG124 = "JSDO: autoApplyChanges is not supported for saveChanges(true) " + + "with a temp-table. Use jsdo.autoApplyChanges = false."; + msg.msgs.jsdoMSG125 = "{1}: The AuthenticationProvider is not managing valid credentials."; + msg.msgs.jsdoMSG126 = "{1}: No support for {2}."; + msg.msgs.jsdoMSG127 = "JSDO: acceptRowChanges() cannot be called for record with _rejected === true."; + + // 500 - 998 are for generic errors + msg.msgs.jsdoMSG500 = "{1}: '{2}' objects must contain a '{3}' property."; + msg.msgs.jsdoMSG501 = "{1}: '{2}' in '{3}' function cannot be an empty string."; + msg.msgs.jsdoMSG502 = "{1}: The '{2}' parameter passed to the '{3}' function has an invalid value for " + + "its '{4}' property."; + msg.msgs.jsdoMSG503 = "{1}: '{2}' must be of type '{3}'."; + msg.msgs.jsdoMSG504 = "{1}: {2} has an invalid value for the '{3}' property."; + msg.msgs.jsdoMSG505 = "{1}: '{2}' objects must have a '{3}' method."; + // use message below if invalid parameter value is an object + msg.msgs.jsdoMSG506 = "{1}: Invalid argument for the {2} parameter in {3} call."; + // use message below if invalid parameter value is a primitive + msg.msgs.jsdoMSG507 = "{1}: '{2}' is an invalid value for the {3} parameter in {4} call."; + msg.msgs.jsdoMSG508 = "JSDOSession: If a JSDOSession object is using the SSO authentication model, " + + "the options object passed to its constructor must include an authProvider property."; + msg.msgs.jsdoMSG509 = "progress.data.getSession: If the authenticationModel is AUTH_TYPE_SSO, " + + "authenticationURI and authProviderAuthenticationModel are required parameters."; + msg.msgs.jsdoMSG510 = "{1}: This session has been invalidated and cannot be used."; + msg.msgs.jsdoMSG511 = "JSDOSession: addCatalog() can only be called if an AuthenticationProvider was passed as an argument or " + + "connect() has been successfully called."; + + msg.msgs.jsdoMSG998 = "JSDO: JSON object in addRecords() must be DataSet or Temp-Table data."; + + msg.getMsgText = function (n, args) { + var text = msg.msgs[n], + i; + if (!text) { + throw new Error("Message text was not found by getMsgText()"); + } + for (i = 1; i < arguments.length; i += 1) { + text = text.replace(new RegExp('\\{' + i + '\\}', 'g'), arguments[i]); + } + + return text; + }; + + progress.data._getMsgText = msg.getMsgText; + + progress.data.PluginManager = {}; + progress.data.PluginManager._plugins = {}; + + progress.data.PluginManager.addPlugin = function(name, plugin) { + if (progress.data.PluginManager._plugins[name] === undefined) { + progress.data.PluginManager._plugins[name] = plugin; + } + else { + throw new Error("A plugin named '" + name + "' is already registered."); + } + }; + + progress.data.PluginManager.getPlugin = function (name) { + return progress.data.PluginManager._plugins[name]; + }; + + progress.data.JSIndexEntry = function JSIndexEntry(index) { + this.index = index; + }; + + progress.data.JSTableRef = function JSTableRef(jsdo, tableName) { + this._jsdo = jsdo; + this._name = tableName; + this._schema = null; + this._primaryKeys = null; + this._fields = null; + this._processed = {}; + this._visited = false; + + // record is used to represent the current record for a table reference + this.record = null; + + // Data structure + this._data = []; + this._index = {}; + this._hasEmptyBlocks = false; + + // Arrays to keep track of changes + this._beforeImage = {}; + this._added = []; + this._changed = {}; + this._deleted = []; + this._lastErrors = []; + this._convertForServer; + + this._createIndex = function () { + var i, block, id, idProperty; + this._index = {}; + this._hasEmptyBlocks = false; + for (i = 0; i < this._data.length; i += 1) { + block = this._data[i]; + if (!block) { + this._hasEmptyBlocks = true; + continue; + } + id = this._data[i]._id; + if (!id) { + idProperty = this._jsdo._resource.idProperty; + if (typeof(idProperty) == "string") { + id = this._data[i][idProperty]; + if (!id) { + throw new Error(msg.getMsgText("jsdoMSG114", idProperty, this._name)); + } + id += ""; + } + else { + id = progress.data._getNextId(); + } + this._data[i]._id = id; + } + this._index[id] = new progress.data.JSIndexEntry(i); + } + this._needCompaction = false; + }; + + this._compact = function () { + var newDataArray = [], i, block; + + for (i = 0; i < this._data.length; i += 1) { + block = this._data[i]; + if (block) { + newDataArray.push(block); + } + } + this._data = newDataArray; + this._createIndex(); + }; + + this._loadBeforeImageData = function (jsonObject, beforeImageJsonIndex, keyFields) { + var prodsBeforeData = jsonObject[this._jsdo._dataSetName]["prods:before"], + tmpIndex = {}, + record, + record2, + recordId, + key, + tmpKeyIndex, + id, + jsrecord, + tmpDataIndex, + tmpDeletedIndex, + i; + + if (prodsBeforeData && prodsBeforeData[this._name]) { + + if ((Object.keys(this._beforeImage).length !== 0) && keyFields && (keyFields.length !== 0)) { + tmpKeyIndex = {}; + for (id in this._beforeImage) { + jsrecord = this._findById(id, false); + + if (jsrecord) { + key = this._getKey(jsrecord.data, keyFields); + tmpKeyIndex[key] = jsrecord.data; + } + } + } + + for (i = 0; i < prodsBeforeData[this._name].length; i++) { + record = prodsBeforeData[this._name][i]; + tmpIndex[record["prods:id"]] = record; + + if (record["prods:rowState"] == "deleted") { + key = undefined; + + if (keyFields && (keyFields.length !== 0)) { + key = this._getKey(record, keyFields); + } + + if (tmpKeyIndex) { + if (tmpKeyIndex[key] !== undefined) { + throw new Error(msg.getMsgText("jsdoMSG032")); + } + } + + if ((tmpDataIndex === undefined) && keyFields && (keyFields.length !== 0)) { + tmpDataIndex = {}; + tmpDeletedIndex = {}; + + for (var j = 0; j < this._data.length; j++) { + record2 = this._data[j]; + if (!record2) continue; + + var key2 = this._getKey(record2, keyFields); + tmpDataIndex[key2] = record2; + } + + // We also want to check if _deleted record already exists + for (var j = 0; j < this._deleted.length; j++) { + record2 = this._deleted[j].data; + if (!record2) continue; + + var key2 = this._getKey(record2, keyFields); + tmpDeletedIndex[key2] = record2; + } + } + + // First check to see if this deleted record is already in _deleted array + if (key !== undefined) { + record2 = tmpDeletedIndex[key]; + if (record2 !== undefined) { + // If record is already in _deleted array, then nothing more to do here + continue; + } + } + + if (key !== undefined) { + record2 = tmpDataIndex[key]; + if (record2 !== undefined) { + var jsrecord = this._findById(record2._id, false); + if (jsrecord) jsrecord._remove(false); + record._id = record2._id; + } + } + + if (record._id === undefined) + record._id = progress.data._getNextId(); + var copy = {}; + this._jsdo._copyRecord( + this._tableRef, record, copy); + this._jsdo._deleteProdsProperties(copy); + this._beforeImage[record._id] = copy; + var jsrecord = new progress.data.JSRecord(this, copy); + this._deleted.push(jsrecord); + } + } + } + + // Process data using jsonObject instead of _data + // First check if there is after-data for table. Can be called with just before-image data + var tableObject = jsonObject[this._jsdo._dataSetName][this._name]; + if (tableObject) { + for (var i = 0; i < jsonObject[this._jsdo._dataSetName][this._name].length; i++) { + record = jsonObject[this._jsdo._dataSetName][this._name][i]; + recordId = undefined; + if (beforeImageJsonIndex && record["prods:id"]) { + recordId = beforeImageJsonIndex[record["prods:id"]]; + } + switch (record["prods:rowState"]) { + case "created": + if (recordId === undefined) { + recordId = record._id; + } + + // If recordId and record._id are undefined, the record was not processed + if (recordId !== undefined) { + this._beforeImage[recordId] = null; + this._added.push(recordId); + } + break; + case "modified": + var beforeRecord = tmpIndex[record["prods:id"]]; + if (beforeRecord === undefined) { + beforeRecord = {}; + } + + if (recordId === undefined) { + recordId = record._id; + } + // If recordId and record._id are undefined, the record was not processed + if (recordId !== undefined) { + beforeRecord._id = record._id; + + var copy = {}; + this._jsdo._copyRecord( + this._tableRef, beforeRecord, copy); + this._jsdo._deleteProdsProperties(copy); + + this._beforeImage[recordId] = copy; + this._changed[recordId] = record; + + this._beforeImage[beforeRecord._id] = copy; + this._changed[beforeRecord._id] = record; + } + break; + case undefined: + break; // rowState is only specified for records that have changed + default: + throw new Error(msg.getMsgText("jsdoMSG030", + "rowState value in before-image data", "'created' or 'modified'")); + } + } + } + + // Process prods:errors + var prodsErrors = jsonObject[this._jsdo._dataSetName]["prods:errors"]; + if (prodsErrors) { + for (var i = 0; i < prodsErrors[this._name].length; i++) { + var item = prodsErrors[this._name][i]; + var recordId = beforeImageJsonIndex[item["prods:id"]]; + var jsrecord = this._findById(recordId, false); + if (jsrecord) { + jsrecord.data._errorString = item["prods:error"]; + } + } + } + + tmpIndex = null; + }; + + /* + * Clears all data (including any pending changes) in buffer + */ + this._clearData = function () { + this._setRecord(null); + + // Data structure + this._data = []; + this._index = {}; + this._createIndex(); + + // Arrays to keep track of changes + this._beforeImage = {}; + this._added = []; + this._changed = {}; + this._deleted = []; + }; + + this.hasData = function () { + var data; + + // Check if we should return this table with its nested child table's data as nested + if (this._jsdo._nestChildren) { + data = this._getDataWithNestedChildren(this._data); + } + else { + data = this._getRelatedData(); + } + + if (this._hasEmptyBlocks) { + for (var i = 0; i < data.length; i++) { + var block = data[i]; + if (!block) { + return true; + } + } + } + + return data.length !== 0; + }; + + this.getData = function (params) { + var i, + data, + numEmptyBlocks, + newDataArray, + block; + + if (this._needCompaction) { + this._compact(); + } + + if (params && params.filter) { + throw new Error("Not implemented in current version"); + } + // Check if we should return this table with its nested child table's data as nested + else if (this._jsdo._nestChildren) { + data = this._getDataWithNestedChildren(this._data); + } + else { + data = this._getRelatedData(); + } + + if (this._hasEmptyBlocks) { + numEmptyBlocks = 0; + newDataArray = []; + for (i = 0; i < data.length; i += 1) { + block = data[i]; + if (block) { + newDataArray.push(block); + } + else { + numEmptyBlocks++; + } + } + if ((numEmptyBlocks * 100 / this._data.length) >= PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS) + this._needCompaction = true; + + data = newDataArray; + } + else { + // Creates a copy of the data if sort and top are specified + // so that the sorting does not happen in the JSDO memory but + // in a copy of the records + if (params && (params.sort || params.top)) { + newDataArray = []; + for (i = 0; i < data.length; i += 1) { + newDataArray.push(data[i]); + } + data = newDataArray; + } + } + + if (params && (params.sort || params.top)) { + if (params.sort) { + // Converts sort option from Kendo UI to sort option used by the JSDO + sortFields = []; + for (i = 0; i < params.sort.length; i += 1) { + field = params.sort[i].field; + if (params.sort[i].dir == "desc") { + field += ":DESC"; + } + sortFields.push(field); + } + + // Obtain sortObject from sort options to get compare functions + var sortObject = this._processSortFields(sortFields); + if (sortObject.sortFields && sortObject.sortFields.length > 0) { + sortObject.tableRef = this; + data.sort(this._getCompareFn(sortObject)); + } + } + + if (params.top) { + if (typeof(params.skip) == "undefined") { + params.skip = 0; + } + + data = data.splice(params.skip, params.top); + } + } + + return data; + }; + + this._recToDataObject = function (record, includeChildren) { + var array = [record]; + var dataObject = array; + + if (typeof(includeChildren) == 'undefined') { + includeChildren = false; + } + if (this._jsdo._dataSetName) { + dataObject = {}; + dataObject[this._jsdo._dataSetName] = {}; + dataObject[this._jsdo._dataSetName][this._name] = array; + if (includeChildren && this._children.length > 0) { + var jsrecord = this._findById(record._id, false); + if (jsrecord) { + for (var i = 0; i < this._children.length; i++) { + var tableName = this._children[i]; + dataObject[this._jsdo._dataSetName][tableName] = + this._jsdo._buffers[tableName]._getRelatedData(jsrecord); + } + } + } + } + else { + if (this._jsdo._dataProperty) { + dataObject = {}; + dataObject[this._jsdo._dataProperty] = array; + } + } + return dataObject; + }; + + this._recFromDataObject = function (dataObject) { + var data = {}; + if (dataObject) { + if (this._jsdo._dataSetName) { + if (dataObject[this._jsdo._dataSetName]) + data = dataObject[this._jsdo._dataSetName][this._name]; + } + else { + if (this._jsdo._dataProperty) { + if (dataObject[this._jsdo._dataProperty]) + data = dataObject[this._jsdo._dataProperty]; + } + else if (dataObject.data) { + data = dataObject.data; + } + else { + data = dataObject; + } + } + } + + return data instanceof Array ? data[0] : data; + }; + + // Property: schema + this.getSchema = function () { + return this._schema; + }; + this.setSchema = function (schema) { + this._schema = schema; + }; + + // Private method that returns the ABL data type for the specified field + this._getABLType = function (fieldName) { + var i, schema; + + schema = this.getSchema(); + + for (i = 0; i < schema.length; i++) { + if (schema[i].name == fieldName) { + return schema[i].ablType; + } + } + + return undefined; + }; + + // Private method that returns format property (from catalog) for the specified field + this._getFormat = function (fieldName) { + var i, schema; + + schema = this.getSchema(); + + for (i = 0; i < schema.length; i++) { + if (schema[i].name == fieldName) { + return schema[i].format; + } + } + + return undefined; + }; + + + + this.add = function (values) { + return this._add(values, true, true); + }; + + // Alias for add() method + this.create = this.add; + + this._add = function (values, trackChanges, setWorkingRecord) { + if (typeof(trackChanges) == 'undefined') { + trackChanges = true; + } + if (typeof(setWorkingRecord) == 'undefined') { + setWorkingRecord = true; + } + var record = {}, + i, + j, + value, + prefixElement, + name; + + if (typeof values === "undefined") { + values = {}; + } + + // Assign values from the schema + var schema = this.getSchema(); + for (i = 0; i < schema.length; i++) { + var fieldName = schema[i].name; + if (schema[i].type == "array") { + record[fieldName] = []; + if (schema[i].maxItems) { + for (var j = 0; j < schema[i].maxItems; j++) { + record[fieldName][j] = this._jsdo._getDefaultValue(schema[i]); + } + } + + // Assign array values from object parameter + value = values[fieldName]; + if (typeof value != "undefined") { + record[fieldName] = value; + delete values[fieldName]; + } + // Assign values from individual fields from flattened arrays + prefixElement = this._jsdo._getArrayField(fieldName); + if (!record[fieldName]) { + record[fieldName] = []; + } + for (j = 0; j < schema[i].maxItems; j += 1) { + name = prefixElement.name + (j+1); + value = values[name]; + if (typeof value != "undefined") { + if (!this._fields[name.toLowerCase()]) { + // Skip element if a field with the same name exists + // Remove property from object for element since it is not part of the actual schema + delete values[prefixElement.name + (j+1)]; + if (typeof value == 'string' && schema[i].items.type != 'string') { + value = this._jsdo._convertType(value, + schema[i].items.type, + null); + } + record[fieldName][j] = value; + } + } + } + } + else { + record[fieldName] = this._jsdo._getDefaultValue(schema[i]); + } + } + + // Assign values based on a relationship + if (this._jsdo.useRelationships && this._relationship && this._parent) { + if (this._jsdo._buffers[this._parent].record) { + for (var j = 0; j < this._relationship.length; j++) { + record[this._relationship[j].childFieldName] = + this._jsdo._buffers[this._parent].record.data[this._relationship[j].parentFieldName]; + } + } + else + throw new Error(msg.getMsgText("jsdoMSG002", this._parent)); + } + // Assign values from object parameter + for (var v in values) { + record[v] = values[v]; + } + + // Specify _id field - do not use schema default + var id; + var idProperty; + if ((idProperty = this._jsdo._resource.idProperty) !== undefined) { + id = record[idProperty]; + } + if (!id) { + id = progress.data._getNextId(); + } + else { + id += ""; + } + record._id = id; + + if (this.autoSort + && this._sortRecords + && (this._sortFn !== undefined || this._sortObject.sortFields !== undefined)) { + if (this._needsAutoSorting) { + this._data.push(record); + this._sort(); + } + else { + // Find position of new record in _data and use splice + for (var i = 0; i < this._data.length; i++) { + if (this._data[i] === null) continue; // Skip null elements + var ret = this._sortFn ? + this._sortFn(record, this._data[i]) : + this._compareFields(record, this._data[i]); + if (ret == -1) break; + } + this._data.splice(i, 0, record); + } + this._createIndex(); + } + else { + this._data.push(record); + this._index[record._id] = new progress.data.JSIndexEntry(this._data.length - 1); + } + + var jsrecord = new progress.data.JSRecord(this, record); + + // Set record property ignoring relationships + if (setWorkingRecord) + this._setRecord(jsrecord, true); + + if (trackChanges) { + // Save before image + this._beforeImage[record._id] = null; + // End - Save before image + this._added.push(record._id); + } + return jsrecord; + }; + + /* + * Returns records related to the specified jsrecord. + * If jsrecord is not specified the parent working record is used. + */ + this._getRelatedData = function (jsrecord) { + var data = []; + + if (this._data.length === 0) return data; + + if (typeof(jsrecord) == 'undefined') { + if (this._jsdo.useRelationships && this._relationship && this._parent) { + jsrecord = this._jsdo._buffers[this._parent].record; + if (!jsrecord) + throw new Error(msg.getMsgText("jsdoMSG002", this._parent)); + } + } + if (jsrecord) { + // Filter records using relationship + for (var i = 0; i < this._data.length; i++) { + var block = this._data[i]; + if (!block) continue; + + var match = false; + for (var j = 0; j < this._relationship.length; j++) { + match = (jsrecord.data[this._relationship[j].parentFieldName] == + this._data[i][this._relationship[j].childFieldName]); + if (!match) break; + } + if (match) + data.push(this._data[i]); + } + } + else + data = this._data; + + return data; + }; + + + // This method is called on a parent table that has child tables + // where the relationship is specified as NESTED. + // It returns a json array that contains the parent rows. + // If a parent row is involved in nested relationship, + // then references to the child rows are added + // to the parent row in a child table array (providing the nested format) + // We are using the internal jsdo _data arrays, + // and adding a child table array to each parent row that has children. + // Once the caller is done with the nested data, they can call jsdo._unnestData() + // which removes these child table references + this._getDataWithNestedChildren = function (data) { + + // Walk through all the rows and determine if any of its child tables + // should be associated (nested) with the current record + for (var i = 0; i < data.length; i++) { + var parentRecord = data[i]; + + // Now walk thru the parent's children to find any nested children + if (this._children && this._children.length > 0) { + for (var j = 0; j < this._children.length; j++) { + var childBuf = this._jsdo._buffers[this._children[j]]; + + if (childBuf._isNested) { + // If child is nested, then we should walk child records to find matches + for (var k = 0; k < childBuf._data.length; k++) { + var childRecord = childBuf._data[k]; + if (!childRecord) continue; + + var match = false; + for (var m = 0; m < childBuf._relationship.length; m++) { + match = (parentRecord[childBuf._relationship[m].parentFieldName] == + childRecord[childBuf._relationship[m].childFieldName]); + if (!match) break; + } + if (match) { + // Make sure that this parentRecord has an array for its child rows + if (!parentRecord[childBuf._name]) { + parentRecord[childBuf._name] = []; + } + parentRecord[childBuf._name].push(childRecord); + } + + + } // end for; finished adding all child rows for parentRecord + + // The child table may have its own nested children so call recursively + // Use child row array in current parentRecord + if (childBuf._hasNestedChild()) { + childBuf._getDataWithNestedChildren(parentRecord[childBuf._name]); + } + + + } // end if (childBuf._isNested) + } + } + + + } + return data; + + }; + + this._findFirst = function () { + if (this._jsdo.useRelationships && this._relationship && this._parent) { + if (this._jsdo._buffers[this._parent].record) { + // Filter records using relationship + for (var i = 0; i < this._data.length; i++) { + var block = this._data[i]; + if (!block) continue; + + var match = false; + var parentFieldName, childFieldName; + for (var j = 0; j < this._relationship.length; j++) { + parentFieldName = this._relationship[j].parentFieldName; + childFieldName = this._relationship[j].childFieldName; + match = (this._jsdo._buffers[this._parent].record.data[parentFieldName] == + this._data[i][childFieldName]); + if (!match) break; + } + if (match) { + return new progress.data.JSRecord(this, this._data[i]); + } + } + } + } + else { + for (var i = 0; i < this._data.length; i++) { + var block = this._data[i]; + if (!block) continue; + + return new progress.data.JSRecord(this, this._data[i]); + } + } + + + return undefined; + }; + + this._setRecord = function (jsrecord, ignoreRelationships) { + if (jsrecord) { + this.record = jsrecord; + } + else { + this.record = undefined; + } + + // Set child records only if useRelationships is true + if (this._jsdo.useRelationships) { + ignoreRelationships = ((typeof(ignoreRelationships) == 'boolean') && ignoreRelationships); + + if (this._children && this._children.length > 0) { + for (var i = 0; i < this._children.length; i++) { + var childTable = this._jsdo._buffers[this._children[i]]; + if (!ignoreRelationships && this.record && childTable._relationship) { + childTable._setRecord(childTable._findFirst()); + } + else { + childTable._setRecord(undefined, ignoreRelationships); + } + } + } + } + + if (this._jsdo._defaultTableRef) { + this._jsdo.record = this.record; + } + }; + + this.assign = function (values) { + if (this.record) { + return this.record.assign(values); + } + else + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + }; + + // Alias for assign() method + this.update = this.assign; + + this.remove = function () { + if (this.record) { + return this.record._remove(true); + } + else + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + }; + + this._remove = function (bTrackChanges) { + if (this.record) { + return this.record._remove(bTrackChanges); + } + else + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + }; + + this.getId = function () { + if (this.record) { + return this.record.data._id; + } + else + return 0; + }; + + // getErrors() - JSTableRef + this.getErrors = function () { + return this._lastErrors; + }; + + this.getErrorString = function () { + if (this.record) { + return this.record.data._errorString; + } + else + return 0; + }; + + this.findById = function (id) { + return this._findById(id, true); + }; + + this._findById = function (id, setWorkingRecord) { + if (typeof(setWorkingRecord) == 'undefined') { + setWorkingRecord = true; + } + if (id && this._index[id]) { + var record = this._data[this._index[id].index]; + this.record = record ? (new progress.data.JSRecord(this, record)) : null; + if (setWorkingRecord) + this._setRecord(this.record); + return this.record; + } + + if (setWorkingRecord) + this._setRecord(null); + return null; + }; + + /* + * Finds a record in the JSDO memory using the specified function to determine the record. + */ + this.find = function (fn) { + if (typeof(fn) != 'function') { + throw new Error(msg.getMsgText("jsdoMSG003", "find()")); + } + var data = this._getRelatedData(); + + for (var i = 0; i < data.length; i++) { + var block = data[i]; + if (!block) { + continue; + } + this._setRecord(new progress.data.JSRecord(this, data[i])); + var result = fn(this.record); + if (typeof(result) != 'boolean') { + throw new Error(msg.getMsgText("jsdoMSG007", "find()")); + } + if (result) { + return this.record; + } + } + + this._setRecord(null); + return null; + }; + + /* + * Loops through the records + */ + this.foreach = function (fn) { + if (typeof(fn) != 'function') { + throw new Error(msg.getMsgText("jsdoMSG003", "foreach()")); + } + var numEmptyBlocks = 0; + if (this._needCompaction) + this._compact(); + + var data = this._getRelatedData(); + + this._inforeach = true; + for (var i = 0; i < data.length; i++) { + var block = data[i]; + if (!block) { + numEmptyBlocks++; + continue; + } + + this._setRecord(new progress.data.JSRecord(this, data[i])); + var result = fn(this.record); + if ((typeof(result) != 'undefined') && !result) + break; + } + + this._inforeach = false; + + if ((numEmptyBlocks * 100 / this._data.length) >= PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS) + this._needCompaction = true; + }; + + this._equalRecord = function (rec1, rec2, keyFields) { + var field; + var match = true; + for (var i = 0; i < keyFields.length; i++) { + var fieldName = keyFields[i]; + var value1 = rec1[fieldName]; + var value2 = rec2[fieldName]; + + if (!jsdo[tableName].caseSensitive) { + field = jsdo[tableName]._fields[fieldName.toLowerCase()]; + if (field && field.type == "string") { + if (value1 !== undefined && value1 !== null) + value1 = value1.toUpperCase(); + if (value2 !== undefined && value2 !== null) + value2 = value2.toUpperCase(); + } + } + + match = (value1 == value2); + if (!match) return false; + } + return true; + }; + + // Private method to merge changes using merge modes: APPEND, EMPTY, MERGE and REPLACE + this._getKey = function (record, keyFields) { + var keyObject = {}; + for (var i = 0; i < keyFields.length; i++) { + var fieldName = keyFields[i]; + var value = record[fieldName]; + + if (!jsdo[tableName].caseSensitive) { + field = jsdo[tableName]._fields[fieldName.toLowerCase()]; + if (field && field.type == "string") { + if (value !== undefined && value !== null) + value = value.toUpperCase(); + } + } + keyObject[fieldName] = value; + } + return JSON.stringify(keyObject); + }; + + this._getCompareFn = function (sortObject) { + if (typeof sortObject == 'function') { + return function (rec1, rec2) { + if (rec1 === null) return 1; + if (rec2 === null) return -1; + + var jsrec1 = new progress.data.JSRecord(this, rec1); + var jsrec2 = new progress.data.JSRecord(this, rec2); + return sortObject(jsrec1, jsrec2); + }; + } + else return function (rec1, rec2) { + var tableRef = sortObject.tableRef; + var sortFields = sortObject.sortFields; + if (!(sortFields instanceof Array)) return 0; + var sortAscending = sortObject.sortAscending; + + if (rec1 === null) return 1; + if (rec2 === null) return -1; + + var field; + for (var i = 0; i < sortFields.length; i++) { + var fieldName = sortFields[i]; + var value1 = rec1[fieldName]; + var value2 = rec2[fieldName]; + + if (!tableRef.caseSensitive) { + field = tableRef._fields[fieldName.toLowerCase()]; + if (field && field.type == "string") { + if (value1 !== undefined && value1 !== null) + value1 = value1.toUpperCase(); + if (value2 !== undefined && value2 !== null) + value2 = value2.toUpperCase(); + } + } + if (value1 > value2 || (value1 === undefined || value1 === null)) + return sortAscending[i] ? 1 : -1; + else if (value1 < value2 || (value2 === undefined && value2 === null)) + return sortAscending[i] ? -1 : 1; + } + return 0; + }; + }; + + this._sortObject = {}; + this._sortObject.tableRef = this; + this._sortObject.sortFields = undefined; + this._sortObject.sortAscending = undefined; + this._compareFields = this._getCompareFn(this._sortObject); + + // _sortRecords - Tells the table reference whether to sort on add, assign and addRecords + this._sortRecords = true; + // Tells the table reference whether an autoSort is required on an add or assign + this._needsAutoSorting = false; + this._sortFn = undefined; + if ((typeof Object.defineProperty) == 'function') { + this._autoSort = true; + Object.defineProperty( + this, + "autoSort", + { + get: function () { + return this._autoSort; + }, + set: function (value) { + if (value) { + this._autoSort = true; + if (this._sortFn || this._sortObject.sortFields) { + this._sort(); + this._createIndex(); + } + } + else + this._autoSort = false; + }, + enumerable: true, + writeable: true + }); + this._caseSensitive = false; + Object.defineProperty( + this, + "caseSensitive", + { + get: function () { + return this._caseSensitive; + }, + set: function (value) { + if (value) { + this._caseSensitive = true; + } + else + this._caseSensitive = false; + if (this.autoSort && + (this._sortObject.sortFields && !this._sortFn)) { + this._sort(); + this._createIndex(); + } + }, + enumerable: true, + writeable: true + }); + } + else { + this.autoSort = true; + this.caseSensitive = false; // caseSensitive is false by default + } + + this._processSortFields = function (sortFields) { + var sortObject = {}; + if (sortFields instanceof Array) { + sortObject.sortFields = sortFields; + sortObject.sortAscending = []; + sortObject.fields = {}; + for (var i = 0; i < sortObject.sortFields.length; i++) { + var idx; + var fieldName; + var field; + + if (typeof (sortObject.sortFields[i]) != 'string') { + throw new Error(msg.getMsgText("jsdoMSG030", "sort field name", "string element")); + } + if ((idx = sortObject.sortFields[i].indexOf(':')) != -1) { + fieldName = sortObject.sortFields[i].substring(0, idx); + var sortOrder = sortObject.sortFields[i].substring(idx + 1); + switch (sortOrder.toUpperCase()) { + case 'ASCENDING': + case 'ASC': + sortObject.sortAscending[i] = true; + break; + case 'DESCENDING': + case 'DESC': + sortObject.sortAscending[i] = false; + break; + default: + throw new Error(msg.getMsgText("jsdoMSG030", + "sort order '" + sortObject.sortFields[i].substring(idx + 1) + "'", + "ASCENDING or DESCENDING")); + } + } + else { + fieldName = sortObject.sortFields[i]; + sortObject.sortAscending[i] = true; + } + if (fieldName != "_id" && this._fields) { + field = this._fields[fieldName.toLowerCase()]; + if (field) { + if (field.type == "array") + throw new Error(msg.getMsgText("jsdoMSG030", "data type found in sort", + "scalar field")); + fieldName = field.name; + } + else + throw new Error(msg.getMsgText("jsdoMSG031", fieldName)); + } + sortObject.sortFields[i] = fieldName; + sortObject.fields[fieldName] = fieldName; + } + } + else { + sortObject.sortFields = undefined; + sortObject.sortAscending = undefined; + sortObject.fields = undefined; + } + return sortObject; + }; + + this.setSortFields = function (sortFields) { + if (sortFields === undefined || sortFields === null) { + this._sortObject.sortFields = undefined; + this._sortObject.sortAscending = undefined; + } + else if (sortFields instanceof Array) { + var sortObject = this._processSortFields(sortFields); + this._sortObject.sortFields = sortObject.sortFields; + this._sortObject.sortAscending = sortObject.sortAscending; + this._sortObject.fields = sortObject.fields; + + if (this.autoSort) { + this._sort(); + this._createIndex(); + } + } + else + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "setSortFields()")); + }; + + this.setSortFn = function (fn) { + // Check that fn parameter is a function + // Valid values are a function, undefined, or null + // Documentation mentions null as a way to clear the sort function + if (fn && typeof (fn) != 'function') { + throw new Error(msg.getMsgText("jsdoMSG030", "parameter in setSortFn()", + "function parameter")); + } + this._sortFn = fn ? this._getCompareFn(fn) : undefined; + if (this.autoSort) { + this._sort(); + this._createIndex(); + } + }; + + this.sort = function (arg1) { + if (arg1 === undefined || arg1 === null) { + throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "sort()")); + } + if (arguments.length !== 1 || + (!(arg1 instanceof Array) && typeof(arg1) != 'function')) { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "sort()")); + } + + if (arg1 instanceof Array) { + var sortObject = this._processSortFields(arg1); + if (sortObject.sortFields && sortObject.sortFields.length > 0) + this._sort(sortObject); + } + else { + this._sort(arg1); + } + this._createIndex(); + }; + + this._sort = function (arg1) { + if (arguments.length === 0 && + (!this.autoSort || (this._sortFn === undefined && this._sortObject.sortFields === undefined))) + return; + + if (arguments.length === 0) { + if (this._sortFn) { + // Sort using function + this._data.sort(this._sortFn); + } + else { + // Sort using sort fields + this._data.sort(this._compareFields); + } + this._needsAutoSorting = false; + } + else { + if (typeof(arg1) == 'function') { + // Sort using function + this._data.sort(this._getCompareFn(arg1)); + } + else { + // Sort using sort fields + arg1.tableRef = this; + this._data.sort(this._getCompareFn(arg1)); + } + if (this.autoSort) + this._needsAutoSorting = true; + } + }; + + /* + * Reads a JSON object into the JSDO memory for the specified table reference. + */ + this.addRecords = function (jsonObject, addMode, keyFields, trackChanges, isInvoke) { + this._jsdo._addRecords(this._name, jsonObject, addMode, keyFields, trackChanges, isInvoke); + }; + + /* + * Accepts changes for the specified table reference. + */ + this.acceptChanges = function () { + var tableRef = this; + + // First, let's remove any "prods:" properties from created and updated records. + // Don't have to worry about deleted records, since they're going away. + for (var id in tableRef._beforeImage) { + // Create + if (tableRef._beforeImage[id] === null) { + var jsrecord = tableRef._findById(id, false); + if (jsrecord !== null) { + tableRef._jsdo._deleteProdsProperties(jsrecord.data, true); + } + + } + // Update + else if (this._changed[id] !== undefined) { + var jsrecord = this._findById(id, false); + if (jsrecord !== null) { + tableRef._jsdo._deleteProdsProperties(jsrecord.data, true); + } + } + } + + tableRef._processed = {}; + tableRef._added = []; + tableRef._changed = {}; + tableRef._deleted = []; + tableRef._beforeImage = {}; + }; + + /* + * Rejects changes for the specified table reference. + */ + this.rejectChanges = function () { + // Reject changes + for (var id in this._beforeImage) { + if (this._beforeImage[id] === null) { + // Undo create + this._jsdo._undoCreate(this, id); + } + else if (this._changed[id] !== undefined) { + // Undo update + this._jsdo._undoUpdate(this, id, true); + } + else { + // Undo delete + this._jsdo._undoDelete(this, id, true); + } + } + + var tableRef = this; + tableRef._processed = {}; + tableRef._added = []; + tableRef._changed = {}; + tableRef._deleted = []; + }; + + this.hasChanges = function () { + return (Object.keys(this._beforeImage).length !== 0); + }; + + this.getChanges = function () { + var result = []; + for (var id in this._beforeImage) { + var item = {rowState: "", record: null}; + // Create + if (this._beforeImage[id] === null) { + item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_CREATE]; + item.record = this._findById(id, false); + } + // Update + else if (this._changed[id] !== undefined) { + item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_UPDATE]; + item.record = this._findById(id, false); + } + // Delete + else { + item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_DELETE]; + item.record = new progress.data.JSRecord(this, this._beforeImage[id]); + } + result.push(item); + } + return result; + }; + + /* + * Private method to apply changes for the specified table reference. + * If _errorString has been set for a row, row change is rejected. + * If it has not been set, acceptRowChanges() is called. + */ + this._applyChanges = function () { + for (var id in this._beforeImage) { + // Create + if (this._beforeImage[id] === null) { + var jsrecord = this._findById(id, false); + if (jsrecord !== null) { + if (jsrecord.data._rejected + || (jsrecord.data._errorString !== undefined)) { + this._jsdo._undoCreate(this, id); + } + else { + jsrecord.acceptRowChanges(); + } + } + else { + // Record not present in JSDO memory + // Delete after Create + var found = false; + for (var i = 0; i < this._deleted.length; i++) { + found = (this._deleted[i].data._id == id); + if (found) break; + } + if (!found) { + throw new Error(msg.getMsgText("jsdoMSG000", + "Created record appears to be deleted without a delete operation.")); + } + } + } + // Update + else if (this._changed[id] !== undefined) { + var jsrecord = this._findById(id, false); + if (jsrecord !== null) { + // Record found in JSDO memory + if (jsrecord.data._rejected + || (jsrecord.data._errorString !== undefined)) { + this._jsdo._undoUpdate(this, id); + } + else { + jsrecord.acceptRowChanges(); + } + } + else { + // Record not present in JSDO memory + // Delete after Update + if (this._beforeImage[id]._rejected + || (this._beforeImage[id]._errorString !== undefined)) { + this._jsdo._undoDelete(this, id); + } + else { + var found = false; + for (var i = 0; i < this._deleted.length; i++) { + found = (this._deleted[i].data._id == id); + if (found) break; + } + if (!found) { + throw new Error(msg.getMsgText("jsdoMSG000", + "Updated record appears to be deleted without a delete operation.")); + } + } + } + } + // Delete + else { + if (this._beforeImage[id]._rejected + || (this._beforeImage[id]._errorString !== undefined)) { + this._jsdo._undoDelete(this, id); + } + } + } + + var tableRef = this; + tableRef._processed = {}; + tableRef._added = []; + tableRef._changed = {}; + tableRef._deleted = []; + tableRef._beforeImage = {}; + }; + + + /* + * Accepts row changes for the working record at the table reference level. + */ + this.acceptRowChanges = function () { + if (this.record) + return this.record.acceptRowChanges(); + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + }; + + /* + * Rejects row changes for the working record at the table reference level. + */ + this.rejectRowChanges = function () { + if (this.record) + return this.record.rejectRowChanges(); + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + }; + + + /* This method returns true + * if this table has any child tables and at least one of those tables is nested. + * Else if returns false. + */ + this._hasNestedChild = function () { + var hasNestedChild = false; + var childBufObj; + + // If table has children, see if any relationship is NESTED + if (this._children.length > 0) { + for (var i = 0; i < this._children.length; i++) { + childBufObj = this._jsdo._buffers[this._children[i]]; + + if (childBufObj._isNested) { + hasNestedChild = true; + break; + } + } + } + + return hasNestedChild; + }; + }; + + /* + * Returns a JSRecord for the specified JSDO. + * @param jsdo the JSDO + * @param record the values of the record + */ + progress.data.JSRecord = function JSRecord(tableRef, record) { + this._tableRef = tableRef; + this.data = record; + + this.getId = function () { + return this.data._id ? this.data._id : null; + }; + + this.getErrorString = function () { + return this.data._errorString; + }; + + /* + * Saves a copy of the current record to the before image. + */ + this._saveBeforeImageUpdate = function () { + // Save before image + if (this._tableRef._beforeImage[this.data._id] === undefined) { + // this.data._index = index; + var copy = {}; + this._tableRef._jsdo._copyRecord( + this._tableRef, this.data, copy); + this._tableRef._beforeImage[this.data._id] = copy; + } + + if (this._tableRef._changed[this.data._id] === undefined) { + this._tableRef._changed[this.data._id] = this.data; + } + // End - Save before image + }; + + /* + * + */ + this._sortRecord = function (fields) { + var index = this._tableRef._index[this.data._id].index; + var record = this._tableRef._data[index]; + + if (this._tableRef.autoSort + && this._tableRef._sortRecords + && (this._tableRef._sortFn !== undefined + || this._tableRef._sortObject.sortFields !== undefined)) { + + if (this._tableRef._sortObject.fields) { + if (typeof fields == 'string') { + if (this._tableRef._sortObject.fields[fields] === undefined) + return; // Only sort records if the the specified field is in the sort fields + } + else if (fields instanceof Array) { + var found = false; + for (var i = 0; i < fields.length; i++) { + if (this._tableRef._sortObject.fields[fields[i]] !== undefined) { + found = true; + break; + } + } + if (!found) + return; // Only sort records if the the specified fields are in the sort fields + } + } + + if (this._tableRef._needsAutoSorting) { + this._tableRef._sort(); + this._tableRef._createIndex(); + } + else { + // Find position of new record in _data and use splice + for (var i = 0; i < this._tableRef._data.length; i++) { + if (this._tableRef._data[i] === null) continue; // Skip null elements + if (i == index) continue; // Skip changed record + var ret = this._tableRef._sortFn ? + this._tableRef._sortFn(record, this._tableRef._data[i]) : + this._tableRef._compareFields(record, this._tableRef._data[i]); + if (ret == -1) break; + } + + if (i > index) { + i--; + } + if (i != index) { + this._tableRef._data.splice(index, 1); + this._tableRef._data.splice(i, 0, record); + this._tableRef._createIndex(); + } + } + } + }; + + /* + * Assigns the specified values. + * @param record parameter with the record values + */ + this.assign = function (record) { + if (record === undefined) + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "assign() or update()")); + + this._saveBeforeImageUpdate(); + + var fieldName, + i, + j, + value, + schema = this._tableRef.getSchema(), + prefixElement, + name; + + if (record) { + for (i = 0; i < schema.length; i += 1) { + fieldName = schema[i].name; + value = record[fieldName]; + if (typeof value != "undefined") { + if (typeof value == 'string' && schema[i].type != 'string') { + value = this._tableRef._jsdo._convertType(value, + schema[i].type, + schema[i].items ? schema[i].items.type : null); + } + this.data[fieldName] = value; + } + if (schema[i].type === "array") { + // Assign values from individual fields from flattened arrays + prefixElement = this._tableRef._jsdo._getArrayField(fieldName); + if (!this.data[fieldName]) { + this.data[fieldName] = []; + } + for (j = 0; j < schema[i].maxItems; j += 1) { + name = prefixElement.name + (j+1); + value = record[name]; + if (typeof value != "undefined") { + // Skip element if a field with the same name exists + if (!this._tableRef._fields[name.toLowerCase()]) { + if (typeof value == 'string' && schema[i].items.type != 'string') { + value = this._tableRef._jsdo._convertType(value, + schema[i].items.type, + null); + } + this.data[fieldName][j] = value; + } + } + } + } + } + + this._sortRecord(); + } + return true; + }; + + // Alias for assign() method + this.update = this.assign; + + /* + * Removes the JSRecord. + */ + this.remove = function () { + return this._remove(true); + }; + + this._remove = function (bTrackChanges) { + if (typeof(bTrackChanges) == 'undefined') { + bTrackChanges = true; + } + + var index = this._tableRef._index[this.data._id].index; + var jsrecord = this._tableRef._findById(this.data._id, false); + + if (bTrackChanges) { + // Save before image + var record = this._tableRef._beforeImage[this.data._id]; + if (record === undefined) { + // Record does not exist in the before image + this.data._index = index; + this._tableRef._beforeImage[this.data._id] = this.data; + } + else { + // Record exists in the before image + if (record) { + // Record is not null - a null entry in the before image indicates + // corresponds to an add + // Save the index of the record + // so that an undo would restore the record in the same position in _data + record._index = index; + } + } + // End - Save before image + this._tableRef._deleted.push(jsrecord); + } + + // Set entry to null instead of removing entry - index requires positions to be persistent + this._tableRef._data[index] = null; + this._tableRef._hasEmptyBlocks = true; + delete this._tableRef._index[this.data._id]; + + // Set record property + this._tableRef._setRecord(null); + + return true; + }; + + /* + * Accepts row changes for the specified record. + */ + this.acceptRowChanges = function () { + var id = this.data._id; + if (this._tableRef._beforeImage[id] !== undefined) { + if (this.data._rejected) { + throw new Error(msg.getMsgText("jsdoMSG127")); + } + if (this._tableRef._beforeImage[id] === null) { + // Accept create + // Remove element from _added + for (var i = 0; i < this._tableRef._added.length; i++) { + if (this._tableRef._added[i] == id) { + this._tableRef._added.splice(i, 1); + break; + } + } + this._tableRef._jsdo._deleteProdsProperties(this.data, true); + } + else if (this._tableRef._changed[id] !== undefined) { + // Accept update + delete this._tableRef._changed[id]; + this._tableRef._jsdo._deleteProdsProperties(this.data, true); + } + else { + // Accept delete + // Remove element from _deleted + for (var i = 0; i < this._tableRef._deleted.length; i++) { + if (this._tableRef._deleted[i].data._id == id) { + this._tableRef._deleted.splice(i, 1); + break; + } + } + } + delete tableRef._beforeImage[id]; + } + }; + + /* + * Rejects row changes for the specified record. + */ + this.rejectRowChanges = function () { + var id = this.data._id; + if (this._tableRef._beforeImage[id] !== undefined) { + if (this._tableRef._beforeImage[id] === null) { + // Undo create + this._tableRef._jsdo._undoCreate(this._tableRef, id); + // Remove element from _added + for (var i = 0; i < this._tableRef._added.length; i++) { + if (this._tableRef._added[i] == id) { + this._tableRef._added.splice(i, 1); + break; + } + } + } + else if (this._tableRef._changed[id] !== undefined) { + // Undo update + this._tableRef._jsdo._undoUpdate(this._tableRef, id, true); + delete this._tableRef._changed[id]; + } + else { + // Undo delete + this._tableRef._jsdo._undoDelete(this._tableRef, id, true); + // Remove element from _deleted + for (var i = 0; i < this._tableRef._deleted.length; i++) { + if (this._tableRef._deleted[i].data._id == id) { + this._tableRef._deleted.splice(i, 1); + break; + } + } + } + delete tableRef._beforeImage[id]; + } + }; + + }; + + /* + * Returns a JSDO for the specified resource. + * @param resNameOrParmObj: the resource name or an object that contains the initial values for the JSDO + * (if this is an object, it should include the name property with the resource name + * @param serviceName : name of service (ignored if 1st param is an object containing the initial values) + */ + progress.data.JSDO = function JSDO(resNameOrParmObj, serviceName) { + var _super = {}; + + if (typeof progress.data.Session == 'undefined') { + throw new Error('ERROR: You must include progress.session.js'); + } + + _super.subscribe = this.subscribe; + + // Override for Observable.subscribe + this.subscribe = function(evt) { + var args = Array.prototype.slice.call(arguments); + if (typeof evt === "string") { + // Aliases for events + switch(evt.toLowerCase()) { + case "beforeread": + args[0] = "beforefill"; + break; + case "afterread": + args[0] = "afterfill"; + break; + } + } + _super.subscribe.apply(this, args); + }; + + this._defineProperty = function (tableName, fieldName) { + Object.defineProperty( + this._buffers[tableName], + fieldName, + { + get: function fnGet() { + var name, + index, + element, + fieldInfo; + if (this.record) { + index = fieldName.indexOf(progress.data.JSDO.ARRAY_INDEX_SEPARATOR); + if (index > 0 && !this._fields[fieldName.toLowerCase()]) { + // Skip element if a field with the same name exists + // Check if field is a flattened array field by quickly checking for the separator + // Extract name and index element + name = fieldName.substring(0, index); + element = fieldName.substring(index + progress.data.JSDO.ARRAY_INDEX_SEPARATOR.length); + fieldInfo = this._fields[name.toLowerCase()]; + if (!isNaN(element) && fieldInfo && (fieldInfo.type === "array")) { + return this.record.data[name][element - 1]; + } + } + return this.record.data[fieldName]; + } + else + return null; + }, + set: function (value) { + var name = fieldName, + index, + element, + fieldInfo; + if (this.record) { + this.record._saveBeforeImageUpdate(); + + try { + index = fieldName.indexOf(progress.data.JSDO.ARRAY_INDEX_SEPARATOR); + if (index > 0 && !this._fields[fieldName.toLowerCase()]) { + // Skip element if a field with the same name exists + name = fieldName.substring(0, index); + element = fieldName.substring(index + progress.data.JSDO.ARRAY_INDEX_SEPARATOR.length); + fieldInfo = this._fields[name.toLowerCase()]; + if (!isNaN(element) && fieldInfo && (fieldInfo.type === "array")) { + this.record.data[name][element - 1] = value; + return; + } + } + this.record.data[fieldName] = value; + } + finally { + this.record._sortRecord(name); + } + } + }, + enumerable: true, + writeable: true + }); + }; + + // Initial values + this._buffers = {}; // Object of table references + this._numBuffers = 0; + this._defaultTableRef = null; + + this._async = true; + this._dataProperty = null; + this._dataSetName = null; + this.operations = []; + this.useRelationships = true; + + this._session = null; + this._needCompaction = false; + + this._hasCUDOperations = false; + this._hasSubmitOperation = false; + this._useSubmit = false; // For saving saveChanges(useSubmit) param + + this.autoApplyChanges = true; // default should be true to support 11.2 behavior + this._lastErrors = []; + this._localStorage = null; + this._convertForServer; + var autoFill = false; + + // Initialize JSDO using init values + if (!arguments[0]) { + throw new Error("JSDO: Parameters are required in constructor."); + } + + if (typeof(arguments[0]) == "string") { + this.name = arguments[0]; +// if ( arguments[1] && (typeof(arguments[1]) == "string") ) +// localServiceName = serviceName; + } + else if (typeof(arguments[0]) == "object") { + var args = arguments[0]; + for (var v in args) { + switch (v) { + case 'autoFill': + autoFill = args[v]; + break; + case 'events': + this._events = {}; + for (var eventName in args[v]) { + this._events[eventName.toLowerCase()] = args[v][eventName]; + } + break; + case 'dataProperty': + this._dataProperty = args[v]; + break; + default: + this[v] = args[v]; + } + } + } + /* error out if caller didn't pass the resource name */ + if ((!this.name) /*|| !(this._session)*/) { + // make this error message more specific? + throw new Error("JSDO: JSDO constructor is missing the value for 'name'"); + } + + /* perform some basic validation on the event object for the proper structure if provided */ + if (this._events) { + if ((typeof this._events) !== 'object') { + throw new Error("JSDO: JSDO constructor event object is not defined as an object"); + } + + /* make sure all the event handlers are sane */ + for (var prop in this._events) { + var evt = this._events[prop]; + if (!(evt instanceof Array)) { + throw new Error('JSDO: JSDO constructor event object for ' + prop + ' must be an array'); + } + evt.forEach(function (el) { + if ((typeof el) !== 'object') { + throw new Error("JSDO: JSDO constuctor event object for " + + prop + " is not defined as an object"); + } + /* listener must have at least fn property defined as a function */ + if ((typeof el.fn) !== 'function') { + throw new Error("JSDO: JSDO event listener for " + prop + " is not a function."); + } + /* scope is optional, but must be an object if provided */ + if (el.scope && (typeof el.scope) !== 'object') { + throw new Error("JSDO: JSDO event listener scope for " + prop + " is not an object."); + } + }); + } + } + + if (this.name) { + // Read resource definition from the Catalog - save reference to JSDO + // Enhance this to deal with multiple services loaded and the same resource + // name is used by more than one service (use the local serviceName var) + this._resource = progress.data.ServicesManager.getResource(this.name); + if (this._resource) { + if (!this.url) + this.url = this._resource.url; + if (!this._dataSetName && this._resource._dataSetName) { + // Catalog defines a DataSet + this._dataSetName = this._resource._dataSetName; + + // Define TableRef property in the JSDO + if (this._resource.dataProperty) { + var buffer = this[this._resource.dataProperty] + = new progress.data.JSTableRef(this, this._resource.dataProperty); + this._buffers[this._resource.dataProperty] = buffer; + } + else { + for (var tableName in this._resource.fields) { + var buffer = this[tableName] + = new progress.data.JSTableRef(this, tableName); + this._buffers[tableName] = buffer; + } + } + } + if (!this._dataProperty && this._resource.dataProperty) + this._dataProperty = this._resource.dataProperty; + + if (!this._dataSetName) { + var tableName = this._dataProperty ? this._dataProperty : ""; + this._buffers[tableName] = new progress.data.JSTableRef(this, tableName); + if (tableName) + this[tableName] = this._buffers[tableName]; + } + + // Add functions for operations to JSDO object + for (var fnName in this._resource.fn) { + this[fnName] = this._resource.fn[fnName]["function"]; + } + // Check if CUD operations have been defined + this._hasCUDOperations = + this._resource.generic["create"] !== undefined + || this._resource.generic["update"] !== undefined + || this._resource.generic["delete"] !== undefined; + this._hasSubmitOperation = this._resource.generic["submit"] !== undefined; + + /* get a session object, using name of the service to look it up in the list of + * sessions maintained by the ServicesManager + */ + if (!this._session) { + var myservice = progress.data.ServicesManager.getService(this._resource.service.name); + this._session = myservice._session; + this._session._pushJSDOs(this); + } + } + else { + throw new Error(msg.getMsgText("jsdoMSG004", this.name)); + } + } + else { + this._buffers[""] = new progress.data.JSTableRef(this, ""); + } + + if (!this._session) { + throw new Error("JSDO: Unable to get user session for resource '" + this.name + "'"); + } + + // Calculate _numBuffers and _defaultTableRef + for (var buf in this._buffers) { + this._buffers[buf]._parent = null; + this._buffers[buf]._children = []; + // The _relationship object is only specified for the child buffer. + // Currently it is limited to only a single relationship. ie. It does not support the + // where the child buffer is involved in more than one data-relation + this._buffers[buf]._relationship = null; + this._buffers[buf]._isNested = false; + if (!this._defaultTableRef) + this._defaultTableRef = this._buffers[buf]; + this._numBuffers++; + } + if (this._numBuffers != 1) + this._defaultTableRef = null; + else { + // record is used to represent the current record for a table reference + // data corresponds to the values (JSON object) of the data + this.record = null; + } + + // Define caseSensitive property at the JSDO level + if ((typeof Object.defineProperty) == 'function') { + this._caseSensitive = false; // caseSensitive is false by default + Object.defineProperty( + this, + "caseSensitive", + { + get: function () { + return this._caseSensitive; + }, + set: function (value) { + this._caseSensitive = value ? true : false; + + for (var buf in this._buffers) { + this._buffers[buf].caseSensitive = this._caseSensitive; + } + }, + enumerable: true, + writeable: true + }); + this._autoSort = true; // autoSort is true by default + Object.defineProperty( + this, + "autoSort", + { + get: function () { + return this._autoSort; + }, + set: function (value) { + this._autoSort = value ? true : false; + + for (var buf in this._buffers) { + this._buffers[buf].autoSort = this._autoSort; + } + }, + enumerable: true, + writeable: true + }); + } + + // Define _properties property at the JSDO level + this._properties = {}; + if ((typeof Object.defineProperty) == 'function') { + Object.defineProperty( this, + "this._properties", + { + get: function () { + return this._properties; + }, + enumerable: false + } + ); + + } + + + // Set schema for TableRef + if (this._resource && this._resource.fields) { + for (var buf in this._buffers) { + this._buffers[buf]._schema = this._resource.fields[buf]; + this._buffers[buf]._primaryKeys = this._resource.primaryKeys[buf]; + + // Create _fields object used to validate fields as case-insensitive. + this._buffers[buf]._fields = {}; + var fields = this._buffers[buf]._schema; + for (var i = 0; i < fields.length; i++) { + this._buffers[buf]._fields[fields[i].name.toLowerCase()] = fields[i]; + if (typeof(fields[i].origName) !== "undefined") { + if ((typeof(fields[i].origName) !== "string") + || (fields[i].origName.trim() === "")) { + throw new Error(msg.getMsgText("jsdoMSG504", + "JSDO", "Field '" + fields[i].name + "' in resource '" + this._resource.name + "'", "origName")); + } + } + } + + if (this._buffers[buf]._schema && (typeof Object.defineProperty) == 'function') { + // Add fields as properties of the TableRef object + for (var i = 0; i < this._buffers[buf]._schema.length; i++) { + var fieldName = this._buffers[buf]._schema[i].name, + fieldInfo = this._buffers[buf]._schema[i]; + if (typeof(this._buffers[buf][fieldName]) == 'undefined') { + this._defineProperty(buf, fieldName); + } + if (fieldInfo.type === "array") { + for (var j = 0; j < fieldInfo.maxItems; j += 1) { + var name = fieldName + progress.data.JSDO.ARRAY_INDEX_SEPARATOR + (j + 1); + // Skip element if a field with the same name exists + // Only create property if the name is not being used + if (!this._buffers[buf]._fields[name.toLowerCase()]) { + this._defineProperty(buf, name); + } + } + } + } + } + } + // Set schema for when dataProperty is used but not specified via the catalog + if (this._defaultTableRef + && !this._defaultTableRef._schema + && this._resource.fields[""]) { + this._defaultTableRef._schema = this._resource.fields[""]; + } + } + else { + if (this._defaultTableRef) + this._defaultTableRef._schema = []; + } + + // Set isNested property + if (this._numBuffers > 1) { + for (var buf in this._buffers) { + var fields = []; + var found = false; + for (var i = 0; i < this._buffers[buf]._schema.length; i++) { + var field = this._buffers[buf]._schema[i]; + + if (field.items + && field.type == "array" && field.items.$ref) { + if (this._buffers[field.name]) { + found = true; + this._buffers[field.name]._isNested = true; + } + } + else + fields.push(field); + } + // Replace list of fields - removing nested datasets from schema + if (found) + this._buffers[buf]._schema = fields; + } + } + + // Process relationships + if (this._resource && this._resource.relations) { + for (var i = 0; i < this._resource.relations.length; i++) { + var relationship = this._resource.relations[i]; + + // Set relationship information ignoring self-referencing (recursive) relationships + if (relationship.childName + && relationship.parentName + && (relationship.childName !== relationship.parentName)) { + // Set casing of fields in relationFields to be the same as in the schema + if (relationship.relationFields instanceof Array) { + for (var j = 0; j < relationship.relationFields.length; j++) { + var fieldName; + var field; + if (this._buffers[relationship.parentName]._fields) { + fieldName = relationship.relationFields[j].parentFieldName; + field=this._buffers[relationship.parentName]._fields[fieldName.toLowerCase()]; + if (field) { + relationship.relationFields[j].parentFieldName = field.name; + } + else + throw new Error(msg.getMsgText("jsdoMSG010", fieldName)); + } + if (this._buffers[relationship.childName]._fields) { + fieldName = relationship.relationFields[j].childFieldName; + field=this._buffers[relationship.childName]._fields[fieldName.toLowerCase()]; + if (field) { + relationship.relationFields[j].childFieldName = field.name; + } + else + throw new Error(msg.getMsgText("jsdoMSG010", fieldName)); + } + } + } + this._buffers[relationship.childName]._parent = relationship.parentName; + this._buffers[relationship.childName]._relationship = relationship.relationFields; + this._buffers[relationship.parentName]._children.push(relationship.childName); + } + } + } + + this._getDefaultValue = function (field) { + var defaultValue, + t, m, d, + isDate = false; + + if ((field.type === "string") + && field.format + && (field.format.indexOf("date") !== -1) + && (field["default"])) { + isDate = true; + } else if ((field.type === "array") + && field.ablType + && (field.ablType.indexOf("DATE") != -1) + && (field["default"])) { + isDate = true; + } else { + defaultValue = field["default"]; + } + + if (isDate) { + switch (field["default"].toUpperCase()) { + case "NOW": + defaultValue = new Date().toISOString(); + break; + case "TODAY": + t = new Date(); + m = String((t.getMonth() + 1)); + if (m.length === 1) { + m = '0' + m; + } + d = String((t.getDate())); + if (d.length === 1) { + d = '0' + d; + } + defaultValue = t.getFullYear() + '-' + m + '-' + d; + break; + default: + defaultValue = field["default"]; + } + } + + return defaultValue; + }; + + // Method to calculate the element information of an array given the name, index, and value + // Parameters: + // arrayFieldName The name o the field + // index Optional parameter - if index is null/undefined the name of the element is the prefix + // value Optional parameter + this._getArrayField = function (arrayFieldName, index, value) { + var element = {}; + // ABL arrays are 1-based + element.name = arrayFieldName + progress.data.JSDO.ARRAY_INDEX_SEPARATOR + ((index >= 0) ? (index + 1) : ""); + element.value = value ? value[index] : undefined; + return element; + }; + + this.isDataSet = function () { + return this._dataSetName ? true : false; + }; + + /* handler for invoke operation complete */ + this._invokeComplete = function (jsdo, success, request) { + // only fire on async requests + if (request.async && request.fnName) { + jsdo.trigger('afterInvoke', request.fnName, jsdo, success, request); + } + + if (request.deferred) { + if (success) { + request.deferred.resolve(jsdo, success, request); + } + else { + request.deferred.reject(jsdo, success, request); + } + } + }; + + /* handler for invoke operation success */ + this._invokeSuccess = function (/* jsdo, success, request */) { + // do nothing + }; + + /* handler for invoke operation error */ + this._invokeError = function (/* jsdo, success, request */) { + // do nothing + }; + + /* + * Performs an HTTP request using the specified parameters. This is + * used to perform remote calls for the JSDO for operations defined. + * + */ + this._httpRequest = function (xhr, method, url, reqBody, request) { + + function afterOpenRequest() { + var input = null; + if (reqBody) { + xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); + input = JSON.stringify(reqBody); + } + + try { + xhr.send(input); + } catch (e) { + request.success = false; + request.exception = e; + // let Session check for online/offline + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + } + } + + // if xhr wasn't passed we'll create our own since this is an invoke operation + // if xhr is passed, then it is probably a CRUD operation which is setup with XHR + // in call to session + if (!xhr) { + xhr = new XMLHttpRequest(); + + // only setup the callback handlers if we're responsible for creating the + // xhr call which happens on invoke operations...which is the normal case + // the CRUD operations setup their own callbacks and they have their own + // event handlers so we don't use them here. + xhr.onCompleteFn = this._invokeComplete; + xhr.onSuccessFn = this._invokeSuccess; + xhr.onErrorFn = this._invokeError; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + + // for invokes we always fire the invoke when doing async + if (request.async && request.fnName) { + this.trigger('beforeInvoke', request.fnName, this, request); + } + + // For Invoke operations, wrap reqBody in a request object + // This is not required for CRUD operations since the whole + // reqBody is mapped to the parameter + if (reqBody) { + if (this._resource && this._resource.service) { + var useRequest = this._resource.service.useRequest; + if (this._resource.service.settings + && this._resource.service.settings.useRequest !== undefined) { + useRequest = this._resource.service.settings.useRequest; + } + if (useRequest) { + reqBody = {request: reqBody}; + } + } + } + } + + xhr.request = request; + xhr.jsdo = this; + request.jsdo = this; + request.xhr = xhr; + + this._session._openRequest(xhr, method, url, request.async, afterOpenRequest); + + return request; // Note: for the async case, this does not give us exactly the same behavior + // as when afterOpenRequest is called synchronously, because this returns + // request before its xhr has had its open() called + }; + + + // This method currently is just used by the JSDOReadService. + // It returns data in its non-nested (default) format + this._getDataObject = function () { + var dataObject = {}; + if (this._dataSetName) { + dataObject[this._dataSetName] = {}; + + var oldUseRelationships = this.useRelationships; + // Turn off useRelationships so that getData() returns all the records + try { + this.useRelationships = false; + for (var buf in this._buffers) { + dataObject[this._dataSetName][buf] = this._buffers[buf].getData(); + } + } + finally { + // Restore useRelationships + this.useRelationships = oldUseRelationships; + } + } + else { + if (this._dataProperty) { + dataObject[this._dataProperty] = this.getData(); + } + else + return this.getData(); // Array + } + return dataObject; + }; + + + // This method currently is just used by the JSDOReadService. + // Now that the JSDO Services support nested data, we want to return data nested for those + // relationships that are marked nested. + // + // This method returns a data object containing the nested data. + // If a parent row is involved in nested relationship, + // then references to its child rows are added to the parent row in a child table array + // (providing the nested format). + // We are using the internal jsdo _data arrays, + // and adding a child table array to each parent row that has children. + // Once the caller is done with the nested data, + // they can call jsdo._unnestData() which removes these child table references + // + this._getDataObjectAsNested = function () { + var dataObject = {}; + if (this._dataSetName) { + dataObject[this._dataSetName] = {}; + + try { + // First walk thru all buffers. We need to determine if any of the buffers are + // involved in a nested relationship. If so, we want to return the child's + // data in nested format. + for (var buf in this._buffers) { + var bufObj = this._buffers[buf]; + + + // If this is a child table, and its involved in a nested relationship, + // then just skip. + // This table's data will be nested within each parent row when we + // process the parent table. + if (bufObj._isNested) continue; + + this._nestChildren = false; // default to false + + // If table has children, see if any relationship is NESTED + if (bufObj._children.length > 0) { + for (var i = 0; i < bufObj._children.length; i++) { + var childBufObj = this._buffers[bufObj._children[i]]; + + if (childBufObj._isNested) { + this._nestChildren = true; + break; + } + } + } + + dataObject[this._dataSetName][buf] = this._buffers[buf].getData(); + } + } + catch (e) { + throw new Error(msg.getMsgText("jsdoMSG000", e.message)); + } + finally { + // Set back to default avlue + this._nestChildren = false; + } + } + else { + if (this._dataProperty) { + dataObject[this._dataProperty] = this.getData(); + } + else + return this.getData(); // Array + } + return dataObject; + }; + + + // This method is used in conjunction with _getDataObjectAsNested() in the JSDOReadService. + // _getDataObjectAsNested() adds arrays of child row references to their parent rows. + // Once the JSDOReadService has done its data mapping, we need to remove the references since + // internally the JSDO stores its data in unnested format. + this._unnestData = function () { + + if (this._dataSetName) { + var parentRecord; + var bufObj; + var childBufObj; + + // First walk thru all buffers. We need to determine if any of the buffers are parent + // buffers involved in a nested relationship. If so, then we'll look for any child row arrays + // to delete + for (var buf in this._buffers) { + bufObj = this._buffers[buf]; + + // If we know this table has at least one nested child table, we'll walk thru + // all its rows to determine if the rows have any child row arrays. + // It's more efficient to just walk thru the parent row list once, so we'll + // check for all child row arrays here + + if (bufObj._hasNestedChild()) { + // Now must walk thru the parent rows and delete any child row arrays + for (var i = 0; i < bufObj._data.length; i++) { + parentRecord = bufObj._data[i]; + + for (var j = 0; j < bufObj._children.length; j++) { + childBufObj = this._buffers[bufObj._children[j]]; + + if (parentRecord[childBufObj._name]) { + delete parentRecord[childBufObj._name]; + } + } + + } + } + } // end for + } + }; + + + this._recToDataObject = function (record, includeChildren) { + if (this._defaultTableRef) + return this._defaultTableRef._recToDataObject(record, includeChildren); + throw new Error(msg.getMsgText("jsdoMSG001", "_recToDataObject()")); + }; + + this._recFromDataObject = function (dataObject) { + if (this._defaultTableRef) + return this._defaultTableRef._recFromDataObject(dataObject); + throw new Error(msg.getMsgText("jsdoMSG001", "_recFromDataObject()")); + }; + + this.add = function (obj) { + if (this._defaultTableRef) + return this._defaultTableRef.add(obj); + throw new Error(msg.getMsgText("jsdoMSG001", "add() or create()")); + }; + + // Alias for add() method + this.create = this.add; + + this.hasData = function () { + for (var buf in this._buffers) { + if (this._buffers[this._buffers[buf]._name].hasData()) + return true; + } + return false; + }; + + this.getData = function (params) { + if (this._defaultTableRef) + return this._defaultTableRef.getData(params); + throw new Error(msg.getMsgText("jsdoMSG001", "getData()")); + }; + + this.getSchema = function () { + if (this._defaultTableRef) + return this._defaultTableRef.getSchema(); + throw new Error(msg.getMsgText("jsdoMSG001", "getSchema()")); + }; + + this.findById = function (id) { + if (this._defaultTableRef) + return this._defaultTableRef.findById(id); + throw new Error(msg.getMsgText("jsdoMSG001", "findById()")); + }; + + this._convertType = function (value, type, itemType) { + if ((typeof value != 'string') || (type === null)) return value; + var result = value; + try { + if (type == 'array') { + var result = []; + + value = value.slice(1, value.length - 1); + var elements = value.split(','); + var convertItem = (itemType && (itemType != 'string')); + for (var i = 0; i < elements.length; i++) { + result[i] = convertItem ? this._convertType(elements[i], itemType, null) : elements[i]; + } + } + else if (type == 'integer') { + result = parseInt(value); + } + else if (type == 'number') { + result = parseFloat(value); + } + else { + result = value; + } + } + catch (e) { + throw new Error(msg.getMsgText("jsdoMSG000", + "Error converting string to native type: " + e.message)); + } + return result; + }; + + this.assign = function (values) { + if (this._defaultTableRef) { + return this._defaultTableRef.assign(values); + } + else + throw new Error(msg.getMsgText("jsdoMSG001", "assign() or update()")); + }; + + // Alias for assign() method + this.update = this.assign; + + this.remove = function () { + if (this._defaultTableRef) { + return this._defaultTableRef.remove(); + } + else + throw new Error(msg.getMsgText("jsdoMSG001", "remove()")); + }; + + this.getId = function () { + if (this._defaultTableRef) + return this._defaultTableRef.getId(); + throw new Error(msg.getMsgText("jsdoMSG001", "getId()")); + }; + + // getErrors() - JSDO + this.getErrors = function () { + if (this._defaultTableRef) + return this._defaultTableRef.getErrors(); + throw new Error(msg.getMsgText("jsdoMSG001", "getErrors()")); + }; + + this.getErrorString = function () { + if (this._defaultTableRef) + return this._defaultTableRef.getErrorString(); + throw new Error(msg.getMsgText("jsdoMSG001", "getErrorString()")); + }; + + /* + * Finds a record in the JSDO memory using the specified function to determine the record. + */ + this.find = function (fn) { + if (this._defaultTableRef) + return this._defaultTableRef.find(fn); + throw new Error(msg.getMsgText("jsdoMSG001", "find()")); + }; + + this.foreach = function (fn) { + if (this._defaultTableRef) + return this._defaultTableRef.foreach(fn); + throw new Error(msg.getMsgText("jsdoMSG001", "foreach()")); + }; + + this.setSortFields = function (sortFields) { + if (this._defaultTableRef) + return this._defaultTableRef.setSortFields(sortFields); + throw new Error(msg.getMsgText("jsdoMSG001", "setSortFields()")); + }; + + this.setSortFn = function (fn) { + if (this._defaultTableRef) + return this._defaultTableRef.setSortFn(fn); + throw new Error(msg.getMsgText("jsdoMSG001", "setSortFn()")); + }; + + this.sort = function (arg1) { + if (this._defaultTableRef) + return this._defaultTableRef.sort(arg1); + throw new Error(msg.getMsgText("jsdoMSG001", "sort()")); + }; + + this._clearErrors = function () { + this._lastErrors = []; + for (var buf in this._buffers) { + this._buffers[buf]._lastErrors = []; + } + }; + + /** + * setAllRecordsRejected + * + * Sets _allRecordsRejected flag to indicate whether all records have been rejected + * in a saveChanges() call. + * If changes are specified as an array, the changes are used to calculate the flag. + * + * @param {*} param - Array with changes or boolean with value + */ + this._setAllRecordsRejected = function (param) { + var changes, + hasErrors, + hasRejected, + hasCommittedRecords, + i; + + // Note: This function is a single one-stop convenient function to set + // _allRecordsRejected and _someRecordsRejected. + // This logic can be optimized by setting the flags while processing the response. + if (param instanceof Object) { + if (param instanceof Array) { + changes = param; + hasErrors = false; + + this._allRecordsRejected = false; + this._someRecordsRejected = false; + + for (var buf in this._buffers) { + if (this._buffers[buf]._lastErrors.length > 0) { + hasErrors = true; + } + } + if (hasErrors) { + this._allRecordsRejected = true; + this._someRecordsRejected = true; + + for (i = 0; i < changes.length; i += 1) { + if (changes[i].record && !changes[i].record.data._rejected) { + this._allRecordsRejected = false; + return; + } + } + } else if (changes.length > 0) { + this._allRecordsRejected = true; + this._someRecordsRejected = false; + hasCommittedRecords = false; + + for (i = 0; i < changes.length; i += 1) { + if (changes[i].record) { + if (changes[i].record.data._rejected) { + this._someRecordsRejected = true; + } else { + hasCommittedRecords = true; + } + } + } + if (hasCommittedRecords && !this._someRecordsRejected) { + this._allRecordsRejected = false; + } + } + } else { + if (param.operations instanceof Array) { + if (param.operations.length > 0 + && !param.operations[0].success) { + // First operation failed + this._allRecordsRejected = true; + this._someRecordsRejected = true; + + for (i = 0; i < param.operations.length; i += 1) { + if (param.operations[i].success) { + this._allRecordsRejected = false; + return; + } + } + } else { + // Not all operations were rejected + this._allRecordsRejected = false; + this._someRecordsRejected = false; + + for (i = 0; i < param.operations.length; i += 1) { + if (!param.operations[i].success) { + this._someRecordsRejected = true; + return; + } + } + } + } + } + } else { + // Possible values: true, false, undefined + this._allRecordsRejected = param; + this._someRecordsRejected = param; + } + }; + + /* + * Loads data from the HTTP resource. + */ + this.fill = function () { + var objParam, + promise, + properties, + mapping; + + // Clear errors before sending request + this._clearErrors(); + + // Reset _allRecordsRejected + this._setAllRecordsRejected(undefined); + + // Process parameters + if (arguments.length !== 0) { + // Call to fill() has parameters + if (typeof(arguments[0]) == 'function') { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "fill() or read()")); + } + + properties = this.getMethodProperties("read"); + + // Get plugin if mappingType is not undefined, null, or "" + if (properties && properties.mappingType) { + mapping = progress.data.PluginManager.getPlugin(properties.mappingType); + if (!mapping) { + throw new Error(msg.getMsgText("jsdoMSG118", properties.mappingType)); + } + } + + // fill( string); + var filter; + if (arguments[0] === null || arguments[0] === undefined) { + filter = ""; + } + else if (typeof(arguments[0]) == "string") { + filter = arguments[0]; + objParam = {filter: filter}; + } + else if (typeof(arguments[0]) == "object") { + // options + // ablFilter, id, top, skip, sort + + // Use plugin if mappingType is not undefined, null, or "" + if (mapping) { + if (typeof(mapping.requestMapping) === "function") { + objParam = mapping.requestMapping(this, arguments[0], { operation: "read" }); + } + else { + objParam = arguments[0]; + } + } + else { + if (properties.capabilities) { + throw new Error(msg.getMsgText("jsdoMSG119")); + } + objParam = arguments[0]; + } + } + else { + throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "fill() or read()")); + } + } + else { + // fill(); + objParam = null; + } + + var xhr = new XMLHttpRequest(); + var request = { + xhr: xhr, + jsdo: this, + objParam: objParam + }; + + xhr.request = request; + xhr.jsdo = this; + + xhr.onSuccessFn = this._fillSuccess; + xhr.onErrorFn = this._fillError; + xhr.onCompleteFn = this._fillComplete; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + + this.trigger("beforeFill", this, request); + + if (this._resource) { + if (typeof(this._resource.generic.read) == "function") { + xhr.objParam = objParam; + this._resource.generic.read.call(this, xhr, this._async); + if (xhr.request.deferred) { + promise = xhr.request.deferred.promise(); + } + } + else { + throw new Error("JSDO: READ operation is not defined."); + } + } + else { + // Old approach to call READ + this._session._openRequest(xhr, 'GET', this.url, this._async); + try { + xhr.send(null); + } + catch (e) { + request.exception = e; + // get the Client Context ID (AppServer ID) + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + } + } + + // This is the scenario where the read.call did not reach server. i.e., + // some problem in between making successful call to server and we are + // completing the fill() operation with necessary cleanup operations + if (request.success == false && request.exception) { + if ((typeof xhr.onErrorFn) == 'function') { + xhr.onErrorFn(xhr.jsdo, request.success, request); + } + + // get the Client Context ID (AppServer ID) + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + + if ((typeof xhr.onCompleteFn) == 'function') { + xhr.onCompleteFn(xhr.jsdo, request.success, request); + } + } + + return promise; + }; + + // Alias for fill() method + this.read = this.fill; + + /* + * Clears all data (including any pending changes) for each buffer in JSDO + */ + this._clearData = function () { + for (var buf in this._buffers) { + this._buffers[buf]._clearData(); + } + }; + + /* + * Executes a CRUD operation using the built-in API. + */ + this._execGenericOperation = function (operation, objParam, request, + onCompleteFn, onSuccessFn, onErrorFn) { + + var xhr = new XMLHttpRequest(); + request.xhr = xhr; + request.jsdo = this; + request.objParam = objParam; + request.operation = operation; + xhr.jsdo = this; + xhr.onCompleteFn = onCompleteFn; + xhr.onSuccessFn = onSuccessFn; + xhr.onErrorFn = onErrorFn; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + xhr.request = request; + + this._convertRequestData(objParam); + + var operationStr; + switch (operation) { + case progress.data.JSDO._OP_READ: + case progress.data.JSDO._OP_CREATE: + case progress.data.JSDO._OP_UPDATE: + case progress.data.JSDO._OP_DELETE: + case progress.data.JSDO._OP_SUBMIT: + operationStr = PROGRESS_JSDO_OP_STRING[operation]; + break; + default: + throw new Error("JSDO: Unexpected operation " + operation + " in HTTP request."); + } + + if (this._resource) { + if (typeof(this._resource.generic[operationStr]) == "function") { + xhr.objParam = objParam; + this._resource.generic[operationStr](xhr, this._async); + } + else { + // "JSDO: {1} operation is not defined." + throw new Error(msg.getMsgText("jsdoMSG046", operationStr.toUpperCase() )); + } + } + }; + + // Determines if any fields need a conversion when data sent to backend + this._initConvertForServer = function () { + var i, buf, schema; + + // If set, we're good. Field lists for conversion have already been created + if (this._convertForServer !== undefined) { + return; + } + + this._convertForServer = false; + for (buf in this._buffers) { + schema = this._buffers[buf].getSchema(); + this._buffers[buf]._convertFieldsForServer = []; + this._buffers[buf]._convertForServer = false; + + // Check if any fields need conversion + for (i = 0; i < schema.length; i++) { + if (schema[i].ablType && this._ablTypeNeedsConversion(schema[i].ablType)) { + this._buffers[buf]._convertFieldsForServer.push({name: schema[i].name, + ablType: schema[i].ablType}); + } + } + if (this._buffers[buf]._convertFieldsForServer.length > 0) { + this._convertForServer = true; + this._buffers[buf]._convertForServer = true; + } + } + }; + + this._convertRequestData = function (objParam) { + var buf, + beforeData; + + if (this._convertForServer === false) { + return; + } + + // We know at least one table has a field to convert + for (buf in this._buffers) { + if (this._buffers[buf]._convertForServer) { + if (objParam[this._dataSetName]) { + // First convert after-table + if (objParam[this._dataSetName][buf]) { + this._convertTableData(this._buffers[buf], objParam[this._dataSetName][buf]); + } + + // Now let's convert before-image data + beforeData = objParam[this._dataSetName]["prods:before"]; + if (beforeData && beforeData[buf]) { + this._convertTableData(this._buffers[buf], beforeData[buf]); + } + } + // This is for case where saveChanges(false) is called with no before-image data + else if (objParam[buf]) { + this._convertTableData(this._buffers[buf], objParam[buf]); + } + } + } + }; + + this._convertTableData = function (tableRef, tableData) { + var i; + + for (i = 0; i < tableData.length; i++) { + this._convertRowData(tableRef, tableData[i]); + } + }; + + this._convertRowData = function (tableRef, record) { + var i, + field; + + for (i = 0; i < tableRef._convertFieldsForServer.length; i += 1) { + field = tableRef._convertFieldsForServer[i]; + record[field.name] = this._convertField(record[field.name], field.ablType); + } + }; + + this._convertField = function (value, ablType) { + var result; + + if (value === undefined || value === null) { + return value; + } + + if (value instanceof Array) { + var resultArray = []; + for (var i = 0; i < value.length; i++) { + resultArray[i] = this._convertField(value[i], ablType); + } + return resultArray; + } + + try { + switch (ablType.toUpperCase()) { + case "DATE": + case "DATETIME": + if (typeof value === 'string') { + result = value; + } + else if (value instanceof Date) { + result = this._convertDate(value, ablType.toUpperCase()); + } + else { + throw new Error("Unexpected value for " + ablType.toUpperCase() + "."); + } + break; + default: + result = value; + break; + } + } + catch (e) { + throw new Error(msg.getMsgText("jsdoMSG000", + "Error in _convertField for value: " + value + ". " + e.message)); + } + + return result; + }; + + // Convert Date object to string for DATE and DATETIME ablTypes + // Not necessary to do for DATETIME-TZ since JSON.stringify() will do correct conversion + this._convertDate = function (value, ablType) { + var result = value; + + // DATE format should be in ISO 8601 format yyyy-mm-dd + // DATETIME format should be in ISO 8601 format yyyy-mm-ddThh:mm:ss.sss + if (ablType === "DATE" || ablType === "DATETIME") { + result = progress.util._pad(value.getFullYear(), 4) + '-' + + progress.util._pad(value.getMonth() + 1) + '-' + + progress.util._pad(value.getDate()); + + if (ablType === "DATETIME") { + result = result + "T" + + progress.util._pad(value.getHours()) + ":" + + progress.util._pad(value.getMinutes()) + ":" + + progress.util._pad(value.getSeconds()) + "." + + progress.util._pad(value.getMilliseconds(), 3); + } + } + + return result; + }; + + + this._ablTypeNeedsConversion = function (ablType) { + + var needsConversion = false; + + switch (ablType.toUpperCase()) { + case "DATE": + case "DATETIME": + needsConversion = true; + break; + } + + return needsConversion; + }; + + + + this._undefWorkingRecord = function () { + // Set record property + for (var buf in this._buffers) { + this._buffers[buf]._setRecord(null); + } + }; + + /* + * Saves changes in the JSDO. Save any outstanding changes for CREATES, UPDATE, and DELETEs + */ + this.saveChanges = function (useSubmit) { + var promise, + request; + + if (useSubmit === undefined) { + useSubmit = false; + } + else if (typeof(useSubmit) != 'boolean') { + throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "saveChanges()")); + } + + // _fireCUDTriggersForSubmit() needs to know how saveChanges() was called + this._useSubmit = useSubmit; + + // confirm the availability of the operations required for executing this saveChanges call + // (_checkThatJSDOHasRequiredOperations() throws an error if there's a missing operation, + // which this method deliberately allows to bubble up to the caller) + this._checkThatJSDOHasRequiredOperations(); + + // Don't allow Submit with just a temp-table if autoApplyChanges is true + if ( !this._dataSetName && this._useSubmit && this.autoApplyChanges) { + /* error message: "autoApplyChanges is not supported for submit with a temp-table */ + /* Use jsdo.autoApplyChanges = false." */ + throw new Error(msg.getMsgText("jsdoMSG124")); + } + + // Check if any data being sent to server needs to first be converted + this._initConvertForServer(); + + // Clear errors before sending request + this._clearErrors(); + + // Reset _allRecordsRejected + this._setAllRecordsRejected(undefined); + + request = { + jsdo: this + }; + + this.trigger("beforeSaveChanges", this, request); + + if (useSubmit) { + /* Pass in request object. + * Need to use same request object so before and after saveChanges events + * are in sync in JSDO Submit Service. */ + promise = this._syncDataSetForSubmit(request); + } + else if (this._dataSetName) { + promise = this._syncDataSetForCUD(); + } + else { + promise = this._syncSingleTable(); + } + + return promise; + }; + + /* + * _checkThatJSDOHasRequiredOperations + + This method is intended to be used by the saveChanges() method to determine whether + the JSDO's resource definition includes the operations necessary for executing the + types of changes that are pending in the JSDO. It checks for Submit if saveChanges + was called with useSubmit set to true, otherwise it checks whatever CUD operations are + pending. + The JSDO's internal _useSubmit property must be set correctly before this method + is called + */ + this._checkThatJSDOHasRequiredOperations = function( ) { + var checkedDelete = false, + checkedCreate = false, + checkedUpdate = false, + buf, + tableRef; + + if (!this._hasCUDOperations && !this._hasSubmitOperation) { + throw new Error(msg.getMsgText("jsdoMSG026")); + } + + // Validate the use of Submit + if (this._useSubmit) { + if (!this._hasSubmitOperation) { + // "JSDO: {1} operation is not defined."; + throw new Error(msg.getMsgText("jsdoMSG046", "SUBMIT")); + } + else { + return; + } + } + + if (!this._resource) { + // Need the _resource property to do the validation. If not present, just return + // and let execution run as normal (presumably there will be an error) + return; + } + + // Find the pending operations and make sure they are defined + for (buf in this._buffers) { + + tableRef = this._buffers[buf]; + + if (!checkedDelete && tableRef._deleted.length > 0) { + this._confirmOperationExists( progress.data.JSDO._OP_DELETE ); + checkedDelete = true; + } + + if (!checkedCreate && tableRef._added.length > 0) { + this._confirmOperationExists( progress.data.JSDO._OP_CREATE ); + checkedCreate = true; + } + + if (!checkedUpdate && Object.keys(tableRef._changed).length > 0) { + this._confirmOperationExists( progress.data.JSDO._OP_UPDATE ); + checkedUpdate = true; + } + + if ( checkedDelete && checkedCreate && checkedUpdate ) { + break; + } + } + + }; + + // Determines whether a given operation is defined by the JSDO's resource + // throws an error if it's not defined + this._confirmOperationExists = function(operation) { + var operationStr = PROGRESS_JSDO_OP_STRING[operation]; + if (typeof(this._resource.generic[operationStr]) !== "function") { + // "JSDO: {1} operation is not defined." + throw new Error(msg.getMsgText("jsdoMSG046", operationStr.toUpperCase() )); + } + }; + + this.invoke = function (name, object) { + var request = this[name](object); + if (request.deferred) { + return request.deferred.promise(); + } + + return undefined; + }; + + /* + * Synchronizes changes for a TableRef + * + * @param operation HTTP operation to be performed + * @param tableRef Handle to the TableRef + * @param batch Optional. batch information associated with the sync operation. + * If not specified a new one will be created. Used for saving datasets. + */ + this._syncTableRef = function (operation, tableRef, batch) { + var rowData, + requestData, + jsonObject; + + if (tableRef._visited) return; + tableRef._visited = true; + + //ensure batch object is sane + if (!batch) { + batch = { + operations: [] + }; + } else if (!batch.operations) { + batch.operations = []; + } + + // Before children + // Create parent records before children + switch (operation) { + case progress.data.JSDO._OP_CREATE: + for (var i = 0; i < tableRef._added.length; i++) { + var id = tableRef._added[i]; + var jsrecord = tableRef._findById(id, false); + + if (!jsrecord) continue; + if (tableRef._processed[id]) continue; + tableRef._processed[id] = jsrecord.data; + + rowData = {}; + jsonObject = {}; + + // Make copy of row data, in case we need to convert data for backend.. + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + + if (this.isDataSet()) { + if (this._useBeforeImage("create")) { + jsonObject[this._dataSetName] = {}; + var dataSetObject = jsonObject[this._dataSetName]; + dataSetObject["prods:hasChanges"] = true; + + dataSetObject[tableRef._name] = []; + + // Dont need to send prods:id for create, + // no before table or error table to match + // Dont need to send prods:clientId - since only sending one record + rowData["prods:rowState"] = "created"; + rowData["prods:clientId"] = jsrecord.data._id; + + delete rowData["_id"]; + + dataSetObject[tableRef._name].push(rowData); + } + else { + jsonObject[tableRef._name] = []; + jsonObject[tableRef._name].push(rowData); + } + } + else { + jsonObject = rowData; + } + + + var request = { + operation: operation, + batch: batch, + jsrecord: jsrecord, + jsdo: this + }; + batch.operations.push(request); + + jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); + this.trigger("beforeCreate", this, jsrecord, request); + + this._execGenericOperation( + progress.data.JSDO._OP_CREATE, jsonObject, request, this._createComplete, + this._createSuccess, this._createError); + } + break; + case progress.data.JSDO._OP_UPDATE: + for (var id in tableRef._changed) { + var jsrecord = tableRef._findById(id, false); + + if (!jsrecord) continue; + if (tableRef._processed[id]) continue; + tableRef._processed[id] = jsrecord.data; + + rowData = {}; + jsonObject = {}; + requestData = {}; + + // Make copy of row data, in case we need to convert data for backend.. + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + + var useBeforeImageFormat = false; + if (this.isDataSet()) { + if (this._useBeforeImage("update")) { + useBeforeImageFormat = true; + jsonObject[this._dataSetName] = {}; + var dataSetObject = jsonObject[this._dataSetName]; + dataSetObject["prods:hasChanges"] = true; + dataSetObject[tableRef._name] = []; + + // Dont need to send prods:clientId - since only sending one record + rowData["prods:id"] = jsrecord.data._id; + rowData["prods:rowState"] = "modified"; + rowData["prods:clientId"] = jsrecord.data._id; + delete rowData["_id"]; + + dataSetObject[tableRef._name].push(rowData); + + // Now create before-table data + dataSetObject["prods:before"] = {}; + var beforeObject = dataSetObject["prods:before"]; + beforeObject[tableRef._name] = []; + + var beforeRowData = {}; + // Dont need to send prods:clientId - since only sending one record + beforeRowData["prods:id"] = jsrecord.data._id; + + tableRef._jsdo._copyRecord(tableRef, + tableRef._beforeImage[jsrecord.data._id], beforeRowData); + delete beforeRowData["_id"]; + + beforeObject[tableRef._name].push(beforeRowData); + } + } + + if (!useBeforeImageFormat) { + if (this._resource.service + && this._resource.service.settings + && this._resource.service.settings.sendOnlyChanges) { + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData, + tableRef._beforeImage[jsrecord.data._id]); + + if (this._resource.idProperty) { + requestData[this._resource.idProperty] = + jsrecord.data[this._resource.idProperty]; + } + else { + throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, + " for sendOnlyChanges property")); + } + } + else + requestData = rowData; + + if (this.isDataSet()) { + jsonObject[tableRef._name] = []; + jsonObject[tableRef._name].push(requestData); + } + else { + jsonObject = rowData; + } + } + + var request = { + jsrecord: jsrecord, + operation: operation, + batch: batch, + jsdo: this + }; + batch.operations.push(request); + + jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); + this.trigger("beforeUpdate", this, jsrecord, request); + + this._execGenericOperation( + progress.data.JSDO._OP_UPDATE, jsonObject, request, this._updateComplete, + this._updateSuccess, this._updateError); + } + break; + } + + // Call _syncTableRef on child tables + for (var i = 0; i < tableRef._children.length; i++) { + var childTableName = tableRef._children[i]; + this._syncTableRef( + operation, this._buffers[childTableName], batch); + } + + // After children + // Delete parent records after children + + if (operation == progress.data.JSDO._OP_DELETE) { + for (var i = 0; i < tableRef._deleted.length; i++) { + var id = tableRef._deleted[i]._id; + var jsrecord = tableRef._deleted[i]; + + if (!jsrecord) continue; + tableRef._processed[id] = jsrecord.data; + + rowData = {}; + jsonObject = {}; + requestData = {}; + + // Make copy of row data, in case we need to convert data for backend.. + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + + var useBeforeImageFormat = false; + if (this.isDataSet()) { + if (this._useBeforeImage("delete")) { + useBeforeImageFormat = true; + jsonObject[this._dataSetName] = {}; + var dataSetObject = jsonObject[this._dataSetName]; + dataSetObject["prods:hasChanges"] = true; + + // There is no after tables for deletes, so just create before-table data + dataSetObject["prods:before"] = {}; + var beforeObject = dataSetObject["prods:before"]; + beforeObject[tableRef._name] = []; + + var beforeRowData = {}; + + // Dont need to send prods:id for delete, no after table or error table to match + // Dont need to send prods:clientId - since only sending one record + beforeRowData["prods:rowState"] = "deleted"; + beforeRowData["prods:clientId"] = jsrecord.data._id; + + tableRef._jsdo._copyRecord(tableRef, + tableRef._beforeImage[rowData._id], beforeRowData); + beforeObject[tableRef._name].push(beforeRowData); + } + } + + if (!useBeforeImageFormat) { + if (this._resource.service + && this._resource.service.settings + && this._resource.service.settings.sendOnlyChanges) { + if (this._resource.idProperty) { + requestData[this._resource.idProperty] = + jsrecord.data[this._resource.idProperty]; + } + else { + throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, + " for sendOnlyChanges property")); + } + } + else { + requestData = rowData; + } + + if (this.isDataSet()) { + jsonObject[tableRef._name] = []; + jsonObject[tableRef._name].push(requestData); + } + else { + jsonObject = rowData; + } + } + + var request = { + batch: batch, + jsrecord: jsrecord, + operation: operation, + jsdo: this + }; + + batch.operations.push(request); + + jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); + this.trigger("beforeDelete", this, jsrecord, request); + + this._execGenericOperation( + progress.data.JSDO._OP_DELETE, jsonObject, request, this._deleteComplete, + this._deleteSuccess, this._deleteError); + } + } + }; + + /* + * Returns true if the specified operation type was specified in the catalog as useBeforeImage, + * else it returns false. + */ + this._useBeforeImage = function (opType) { + + for (var idx = 0; idx < this._resource.operations.length; idx++) { + if (this._resource.operations[idx].type == opType) { + return this._resource.operations[idx].useBeforeImage; + } + } + + return false; + }; + + + /* + * Synchronizes changes for a DataSet. This is called when we send over one row at at time + * to Create, Update and Delete methods. + * It handles row with or without before-image data. + */ + this._syncDataSetForCUD = function () { + var batch = { + operations: [] + }, + deferred, + promise; + + if (typeof($) == 'function' && typeof($.Deferred) == 'function') { + deferred = $.Deferred(); + promise = deferred.promise(); + batch.deferred = deferred; + } + + // Process buffers + // Synchronize deletes + for (var buf in this._buffers) { + this._buffers[buf]._visited = false; + } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._syncTableRef( + progress.data.JSDO._OP_DELETE, tableRef, batch); + } + + // Synchronize adds + for (var buf in this._buffers) { + this._buffers[buf]._visited = false; + } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._syncTableRef( + progress.data.JSDO._OP_CREATE, tableRef, batch); + } + + // Synchronize updates + for (var buf in this._buffers) { + this._buffers[buf]._visited = false; + } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._syncTableRef( + progress.data.JSDO._OP_UPDATE, tableRef, batch); + } + + if (this.autoApplyChanges) { + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + tableRef._processed = {}; + tableRef._added = []; + tableRef._changed = {}; + tableRef._deleted = []; + } + } + + // OE00229270 If _async is false, this ensures that afterSaveChanges() is called just once + // We now do this after all operations have been processed + if (!this._async) { + if (this._isBatchComplete(batch)) { + var success = this._isBatchSuccess(batch); + var request = { + batch: batch, + success: success + }; + this._undefWorkingRecord(); + + // Save error messages + this._lastErrors = []; + if (!success && batch.operations) { + this._updateLastErrors(this, batch, null); + } + this._setAllRecordsRejected(batch); + + this._fireAfterSaveChanges(success, request); + } + } + // end OE00229270 + + return promise; + }; + + + /* + * Synchronizes changes for a single table + */ + this._syncSingleTable = function () { + var deferred, promise; + if (!this._defaultTableRef) return; + var tableRef = this._defaultTableRef; + + var batch = { + operations: [] + }; + + if (typeof($) == 'function' && typeof($.Deferred) == 'function') { + deferred = $.Deferred(); + promise = deferred.promise(); + batch.deferred = deferred; + } + + var fireAfterSaveChanges = false; + + // Skip delete for records that were added + // mark them as processed + var addedRecords = {}; + for (var i = 0; i < tableRef._added.length; i++) { + var id = tableRef._added[i]; + addedRecords[id] = id; + } + for (var i = 0; i < tableRef._deleted.length; i++) { + var jsrecord = tableRef._deleted[i]; + if (!jsrecord) continue; + + var id = jsrecord.data._id; + if (addedRecords[id]) { + // Set request object + // Properties async, fnName, objParam, and response + // are not set when the HTTP request is suppressed + var request = { + success: true, + xhr: undefined, + operation: progress.data.JSDO._OP_DELETE, + batch: batch, + jsrecord: jsrecord, + jsdo: this + }; + batch.operations.push(request); + tableRef._processed[id] = jsrecord.data; + + var jsdo = request.jsdo; + try { + request.jsrecord._tableRef.trigger("afterDelete", jsdo, request.jsrecord, + request.success, request); + jsdo.trigger("afterDelete", jsdo, request.jsrecord, request.success, request); + } finally { + request.complete = true; + } + + fireAfterSaveChanges = true; + } + } + addedRecords = null; + + // Synchronize deletes + for (var i = 0; i < tableRef._deleted.length; i++) { + var jsrecord = tableRef._deleted[i]; + if (!jsrecord) continue; + + var id = jsrecord.data._id; + if (tableRef._processed[id]) continue; + + tableRef._processed[id] = jsrecord.data; + fireAfterSaveChanges = false; + + var xhr = new XMLHttpRequest(); + xhr.jsdo = this; + + var request = { + xhr: xhr, + operation: progress.data.JSDO._OP_DELETE, + batch: batch, + jsrecord: jsrecord, + jsdo: this + }; + batch.operations.push(request); + xhr.onCompleteFn = this._deleteComplete; + xhr.onSuccessFn = this._deleteSuccess; + xhr.onErrorFn = this._deleteError; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + xhr.request = request; + + jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); + this.trigger("beforeDelete", this, jsrecord, request); + + var requestData = {}; + if (this._resource.service + && this._resource.service.settings + && this._resource.service.settings.sendOnlyChanges) { + if (this._resource.idProperty) { + requestData[this._resource.idProperty] = jsrecord.data[this._resource.idProperty]; + } + else { + throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, + " for sendOnlyChanges property")); + } + } + else { + // We must copy record in case _convertRowData() needs to make conversion + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData); + } + + if (tableRef._convertForServer) { + this._convertRowData(tableRef, requestData); + } + + if (this._resource) { + if (typeof(this._resource.generic["delete"]) == "function") { + xhr.objParam = requestData; + this._resource.generic["delete"].call(this, xhr, this._async); + } + else { + throw new Error("JSDO: DELETE operation is not defined."); + } + } + else { + this._session._openRequest(xhr, 'DELETE', this.url + '/' + id, true); + try { + xhr.send(null); + } catch (e) { + request.success = false; + request.exception = e; + // let Session check for online/offline + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + } + + } + } + + // Synchronize adds + for (var i = 0; i < tableRef._added.length; i++) { + var id = tableRef._added[i]; + var jsrecord = tableRef._findById(id, false); + var requestData = {}; + + if (!jsrecord) continue; + if (tableRef._processed[id]) continue; + tableRef._processed[id] = jsrecord.data; + fireAfterSaveChanges = false; + + var xhr = new XMLHttpRequest(); + xhr.jsdo = this; + var request = { + xhr: xhr, + jsrecord: jsrecord, + batch: batch, + operation: progress.data.JSDO._OP_CREATE, + jsdo: this + }; + batch.operations.push(request); + xhr.onCompleteFn = this._createComplete; + xhr.onSuccessFn = this._createSuccess; + xhr.onErrorFn = this._createError; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + xhr.request = request; + + jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); + this.trigger("beforeCreate", this, jsrecord, request); + + if (this._resource) { + if (typeof(this._resource.generic.create) == "function") { + this._copyRecord(tableRef, jsrecord.data, requestData); + if (this._resource.idProperty !== undefined && jsrecord.data._id !== undefined) { + // Remove _id when idProperty is set + delete requestData._id; + } + + if (tableRef._convertForServer) { + this._convertRowData(tableRef, requestData); + } + + xhr.objParam = requestData; + + this._resource.generic.create.call(this, xhr, this._async); + } + else { + throw new Error("JSDO: CREATE operation is not defined."); + } + + } + else { + this._session._openRequest(xhr, 'POST', this.url, true); + xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); + this._copyRecord(tableRef, jsrecord.data, requestData); + + if (tableRef._convertForServer) { + this._convertRowData(tableRef, requestData); + } + var input = JSON.stringify(requestData); + + try { + xhr.send(input); + } catch (e) { + request.success = false; + request.exception = e; + // let Session check for online/offline + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + } + + } + } + + // Synchronize updates + for (var id in tableRef._changed) { + var jsrecord = tableRef._findById(id, false); + + if (!jsrecord) continue; + if (tableRef._processed[id]) continue; + tableRef._processed[id] = jsrecord.data; + fireAfterSaveChanges = false; + + var xhr = new XMLHttpRequest(); + var request = { + xhr: xhr, + jsrecord: jsrecord, + operation: progress.data.JSDO._OP_UPDATE, + batch: batch, + jsdo: this + }; + xhr.request = request; + xhr.jsdo = this; + batch.operations.push(request); + xhr.onCompleteFn = this._updateComplete; + xhr.onSuccessFn = this._updateSuccess; + xhr.onErrorFn = this._updateError; + xhr.onreadystatechange = this.onReadyStateChangeGeneric; + + jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); + this.trigger("beforeUpdate", this, jsrecord, request); + + var requestData = {}; + if (this._resource.service + && this._resource.service.settings + && this._resource.service.settings.sendOnlyChanges) { + + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData, + tableRef._beforeImage[jsrecord.data._id]); + + if (this._resource.idProperty) { + requestData[this._resource.idProperty] = jsrecord.data[this._resource.idProperty]; + } + else { + throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, + " for sendOnlyChanges property")); + } + } + else { + // We must copy record in case _convertRowData() needs to make conversion + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData); + } + + if (tableRef._convertForServer) { + this._convertRowData(tableRef, requestData); + } + + if (this._resource) { + if (typeof(this._resource.generic.update) == "function") { + xhr.objParam = requestData; + this._resource.generic.update.call(this, xhr, this._async); + } + else { + throw new Error("JSDO: UPDATE operation is not defined."); + } + } + else { + this._session._openRequest(xhr, 'PUT', this.url + '/' + id, this._async); + xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); + + var input = JSON.stringify(requestData); + + try { + xhr.send(input); + } catch (e) { + request.success = false; + request.exception = e; + // let Session check for online/offline + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + } + } + } + + if (this.autoApplyChanges) { + // Arrays to keep track of changes + tableRef._added = []; + tableRef._changed = {}; + tableRef._deleted = []; + tableRef._processed = {}; + } + + // OE00229270 If _async is false, fire afterSaveChanges() after all operations are processed + if (!this._async) + fireAfterSaveChanges = true; + + if (fireAfterSaveChanges) { + var jsdo = this; + var request = { + batch: batch, + success: true + }; + + // Save error messages + jsdo._lastErrors = []; + if (batch.operations) { + jsdo._updateLastErrors(jsdo, batch, null); + } + + jsdo._undefWorkingRecord(); + jsdo._fireAfterSaveChanges(request.success, request); + } + + return promise; + }; + + /************************************************************************ + * + * Synchronizes changes for a DataSet or a temp-table, sending over the entire change-set + * to saveChanges() on server + * If sync'ing a DataSet, sends over before-image and after-image data. + */ + this._syncDataSetForSubmit = function (request) { + var deferred, + promise, + jsonObject, + completeFn = this._saveChangesComplete, + successFn = this._saveChangesSuccess, + errorFn = this._saveChangesError; + + if (typeof($) == 'function' && typeof($.Deferred) == 'function') { + deferred = $.Deferred(); + promise = deferred.promise(); + request.deferred = deferred; + } + + request.jsrecords = []; + + // First thing to do is to create jsonObject with before and after image data for all + // records in change-set (creates, updates and deletes) + if ( this._dataSetName ) { + jsonObject = this._createChangeSet(this._dataSetName, false, request); + } + else { + // just a temp-table. Need to create it somewhat differently from DS + // (no before and after image data) + jsonObject = this._createTTChangeSet(this._defaultTableRef, request); + successFn = this._saveChangesSuccessTT; // will process success response differently from DS + } + + this._execGenericOperation(progress.data.JSDO._OP_SUBMIT, jsonObject, request, + completeFn, successFn, errorFn); + + return promise; + }; + + /************************************************************************ + * + * Private method that creates a jsonObject with before and after image data for all + * records in change-set (creates, updates and deletes) + * + * Params: dataSetName is required. + * alwaysCreateTable is required. If true, always create table array (even if no data/changes) + * request is optional + */ + this._createChangeSet = function (dataSetName, alwaysCreateTable, request) { + var changeSetJsonObject = {}; + + changeSetJsonObject[dataSetName] = {}; + var dataSetJsonObject = changeSetJsonObject[dataSetName]; + + var hasChanges = dataSetJsonObject["prods:hasChanges"] = this._hasChanges(); + if (hasChanges) { + if ((alwaysCreateTable === true)) { + for (var buf in this._buffers) { + dataSetJsonObject[this._buffers[buf]._name] = []; + } + } + + // First do deletes + //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._addDeletesToChangeSet(tableRef, dataSetJsonObject, request); + } + + // Adds + //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._addCreatesToChangeSet(tableRef, dataSetJsonObject, request); + } + + // Updates + //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._addChangesToChangeSet(tableRef, dataSetJsonObject, request); + } + + // Clear _processed map + for (var buf in this._buffers) { + this._buffers[buf]._processed = {}; + } + } + + // Check if change set is empty + // A saveChanges() with a delete of new record would result in an empty change set + // An empty DataSet is sent to the server to ensure that AfterSaveChanges fires + var keys = Object.keys(changeSetJsonObject[dataSetName]); + if (keys.length == 1 && keys[0] == "prods:hasChanges") { + for (var buf in this._buffers) { + dataSetJsonObject[this._buffers[buf]._name] = []; + } + dataSetJsonObject["prods:hasChanges"] = false; + } + + return changeSetJsonObject; + }; + + /************************************************************************ + * + * Private method that creates a jsonObject for the created and changed records + * in a temp-table. There is no before-image information. This is used in the + * case of a Submit operation when the JSDO is just for a temp-table + * + * Params: dataSetName is required. + * alwaysCreateTable is required. If true, always create table array (even if no data/changes) + * request is optional + */ + this._createTTChangeSet = function (tableRef, request) { + var changeSetJsonObject = {}, + hasChanges, + tempTableJsonObject, + i, + id, + jsrecord; + + changeSetJsonObject[tableRef._name] = []; + tempTableJsonObject = changeSetJsonObject[tableRef._name]; + + hasChanges = this._hasChanges(); + if (hasChanges) { + + // (note that we do not send deleted rows on submit for a temp-table) + + // Adds + for (i = 0; i < tableRef._added.length; i++) { + id = tableRef._added[i]; + jsrecord = tableRef._findById(id, false); + if (jsrecord) { + if ( !tableRef._processed[jsrecord.data._id] ) { + this._addRowToTTChangeSet(tableRef, jsrecord, tempTableJsonObject, + request, "beforeCreate"); + } + } + } + + // changed rows + for (id in tableRef._changed) { + if (tableRef._changed.hasOwnProperty(id)) { + jsrecord = tableRef._findById(id, false); + if (jsrecord) { + if ( !tableRef._processed[jsrecord.data._id] ) { + this._addRowToTTChangeSet(tableRef, jsrecord, tempTableJsonObject, + request, "beforeUpdate"); + } + } + } + } + + // Clear _processed map + tableRef._processed = {}; + } + + return changeSetJsonObject; + }; + + this._addRowToTTChangeSet = function (tableRef, jsrecord, tempTableJsonObject, request, event) { + var rowData = {}; + + tableRef._processed[jsrecord.data._id] = jsrecord.data; + + // Store jsrecord in request object so we can access it when saveChanges completes, + // in order to run afterCreate events + if (typeof(request) != 'undefined') { + request.jsrecords.push(jsrecord); + + // Need to call beforeCreate trigger when saveChanges(true) is called + jsrecord._tableRef.trigger(event, this, jsrecord, request); + this.trigger(event, this, jsrecord, request); + } + + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + delete rowData["_id"]; + + tempTableJsonObject.push(rowData); + }; + + /************************************************************************ + * + * Private method that creates a jsonObject with data and also before image data + * for all records in change-set (creates, updates and deletes) + * + * Params: dataSetName is required. + * It returns jsonObject that can be used as input to addRecords() + */ + this._createDataAndChangeSet = function (dataSetName) { + var jsonObject = {}; + + jsonObject[dataSetName] = {}; + var dataSetJsonObject = jsonObject[dataSetName]; + + /* We always want to create tables (even if there's no data) so we can compare schemas + * of data in local storage to JSDO's schema */ + for (var buf in this._buffers) + dataSetJsonObject[this._buffers[buf]._name] = []; + + if (this._hasChanges()) { + dataSetJsonObject["prods:hasChanges"] = true; + } + + // Add data from each table. This will also add bi data for any created or updated rows + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._addRecordsToObject(tableRef, dataSetJsonObject); + } + + // Now do deletes + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + this._addDeletesToChangeSet(tableRef, dataSetJsonObject); + } + + // Clear _processed map + for (var buf in this._buffers) { + this._buffers[buf]._processed = {}; + } + return jsonObject; + }; + + // This method adds all record for specified table into dataSetJsonObject. + // If record has bi data, it adds that as well + this._addRecordsToObject = function (tableRef, dataSetJsonObject) { + + if (tableRef._data.length > 0 && !dataSetJsonObject[tableRef._name]) + dataSetJsonObject[tableRef._name] = []; + + for (var i = 0; i < tableRef._data.length; i++) { + var record = tableRef._data[i]; + if (!record) continue; + + // Check if record has bi data, can only determine if it's created or changed since + // deleted rows are not in after data + if (this._doesRecordHaveCreateBIData(tableRef, record._id) === true) { + var jsrecord = tableRef._findById(record._id, false); + if (!jsrecord) continue; + if (tableRef._processed[jsrecord.data._id]) continue; + this._addCreatedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject); + } + if (this._doesRecordHaveUpdateBIData(tableRef, record._id) === true) { + var jsrecord = tableRef._findById(record._id, false); + if (!jsrecord) continue; + if (tableRef._processed[jsrecord.data._id]) continue; + this._addChangedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject); + } + else { + if (tableRef._processed[record._id]) continue; + tableRef._processed[record._id] = record; + + var rowData = {}; + + tableRef._jsdo._copyRecord(tableRef, record, rowData); + delete rowData["_id"]; + + dataSetJsonObject[tableRef._name].push(rowData); + } + } + }; + + + // Check if specified after record has bi data for newly created record. + // Returns True if after record has corresponding bi data, else false + this._doesRecordHaveCreateBIData = function (tableRef, id) { + for (var i = 0; i < tableRef._added.length; i++) { + if (tableRef._added[i] === id) + return true; + } + + return false; + }; + + // Check if specified after record has bi data for updated record. + // Returns True if after record has corresponding bi data, else false + this._doesRecordHaveUpdateBIData = function (tableRef, id) { + for (var changedId in tableRef._changed) { + if (changedId === id) + return true; + } + + return false; + }; + + + // If a create, remove or update exists, method returns true, else returns false + this._hasChanges = function () { + var hasChanges = false; + + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + + var hasUpdates = false; + for (var id in tableRef._changed) { + hasUpdates = true; + break; + } + + if (tableRef._deleted.length > 0 || tableRef._added.length > 0 || hasUpdates) { + hasChanges = true; + break; + } + } + + return hasChanges; + }; + + // This method is used when saveChanges() is called, and also when storing data to local storage. + // The request param should be defined for saveChanges(), + // but not needed when storing data to local storage + this._addDeletesToChangeSet = function (tableRef, dataSetJsonObject, request) { + // There is no after table for deletes, so just create before-table data + for (var i = 0; i < tableRef._deleted.length; i++) { + var jsrecord = tableRef._deleted[i]; + + if (!jsrecord) continue; + + if (jsrecord.data + && jsrecord.data._id !== undefined + && tableRef._beforeImage[jsrecord.data._id] === null) { + // Deleted record is for a new record - do not send deleted record to server + continue; + } + + this._addDeletedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); + } + }; + + this._addDeletedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { + tableRef._processed[jsrecord.data._id] = jsrecord.data; + + // Store jsrecord in request object so we can access it when saveChanges completes, + // in order to run afterDelete events + jsrecord.data["prods:rowState"] = "deleted"; + + if (typeof(request) != 'undefined') { + request.jsrecords.push(jsrecord); + + // Need to call beforeDelete trigger if saveChanges(true) is called + jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); + this.trigger("beforeDelete", this, jsrecord, request); + } + + var beforeRowData = {}; + // AppServer will roundtrip this back to jsdo client + beforeRowData["prods:clientId"] = jsrecord.data._id; + beforeRowData["prods:rowState"] = "deleted"; + + var beforeTableJsonObject = this._getTableInBeforeJsonObject(dataSetJsonObject, tableRef._name); + tableRef._jsdo._copyRecord(tableRef, tableRef._beforeImage[jsrecord.data._id], beforeRowData); + delete beforeRowData["_id"]; + + beforeTableJsonObject.push(beforeRowData); + }; + + // This method is used when saveChanges() is called, and also when storing data to local storage. + // The request param should be defined for saveChanges(), + // but not needed when storing data to local storage + this._addCreatesToChangeSet = function (tableRef, dataSetJsonObject, request) { + // There is no before table for creates, so just create after-table data + for (var i = 0; i < tableRef._added.length; i++) { + var id = tableRef._added[i]; + var jsrecord = tableRef._findById(id, false); + if (!jsrecord) continue; + if (tableRef._processed[jsrecord.data._id]) continue; + + this._addCreatedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); + } + }; + + this._addCreatedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { + tableRef._processed[jsrecord.data._id] = jsrecord.data; + + if (!dataSetJsonObject[tableRef._name]) { + dataSetJsonObject[tableRef._name] = []; + } + + // Store jsrecord in request object so we can access it when saveChanges completes, + // in order to run afterCreate events + jsrecord.data["prods:rowState"] = "created"; + + if (typeof(request) != 'undefined') { + request.jsrecords.push(jsrecord); + + // Need to call beforeCreate trigger when saveChanges(true) is called + jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); + this.trigger("beforeCreate", this, jsrecord, request); + } + + var rowData = {}; + // AppServer will roundtrip this back to jsdo client + rowData["prods:clientId"] = jsrecord.data._id; + rowData["prods:rowState"] = "created"; + + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + delete rowData["_id"]; + + dataSetJsonObject[tableRef._name].push(rowData); + }; + + // This method is used when saveChanges() is called, and also when storing data to local storage. + // The request param should be defined for saveChanges(), + // but not needed when storing data to local storage + this._addChangesToChangeSet = function (tableRef, dataSetJsonObject, request) { + // For Changes, there is both before and after table data + for (var id in tableRef._changed) { + var jsrecord = tableRef._findById(id, false); + if (!jsrecord) continue; + if (tableRef._processed[jsrecord.data._id]) continue; + + this._addChangedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); + } + }; + + this._addChangedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { + tableRef._processed[jsrecord.data._id] = jsrecord.data; + + if (!dataSetJsonObject[tableRef._name]) { + dataSetJsonObject[tableRef._name] = []; + } + + // Store jsrecord in request object so we can access it when saveChanges completes, in order + // to run afterUpdate events + jsrecord.data["prods:rowState"] = "modified"; + + if (typeof(request) != 'undefined') { + request.jsrecords.push(jsrecord); + + // Need to call beforeUpdate trigger when saveChanges(true) is called + jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); + this.trigger("beforeUpdate", this, jsrecord, request); + } + + var rowData = {}; + // Required by AppServer in before-image data. Matches before row + rowData["prods:id"] = jsrecord.data._id; + // AppServer will roundtrip this back to jsdo client + rowData["prods:clientId"] = jsrecord.data._id; + rowData["prods:rowState"] = "modified"; + + tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); + delete rowData["_id"]; + + dataSetJsonObject[tableRef._name].push(rowData); + + // Now add before-image data + var beforeTableJsonObject = this._getTableInBeforeJsonObject(dataSetJsonObject, tableRef._name); + var beforeRowData = {}; + // Required by AppServer in before-image data. Matches after row + beforeRowData["prods:id"] = jsrecord.data._id; + + tableRef._jsdo._copyRecord(tableRef, tableRef._beforeImage[jsrecord.data._id], beforeRowData); + //delete beforeRowData["_id"]; + + beforeTableJsonObject.push(beforeRowData); + }; + + + // Private method to get table's json object from the specified dataset json object. + // If it hasn't been created yet, this method creates it. + this._getTableInBeforeJsonObject = function (dataSetJsonObject, tableName) { + if (!dataSetJsonObject["prods:before"]) { + dataSetJsonObject["prods:before"] = {}; + } + var beforeObject = dataSetJsonObject["prods:before"]; + + if (!beforeObject[tableName]) { + beforeObject[tableName] = []; + } + + return beforeObject[tableName]; + }; + + + /********************************************************************* + * + * Reads a JSON object into the JSDO memory. + */ + this.addRecords = function (jsonObject, addMode, keyFields, trackChanges, isInvoke) { + if (this.isDataSet()) { + if (jsonObject instanceof Array) { + if (!this._defaultTableRef) { + throw new Error(msg.getMsgText("jsdoMSG998")); + } + } + else { + if (jsonObject === undefined || jsonObject === null) { + jsonObject = {}; + } + + if (jsonObject[this._dataSetName]) { + jsonObject = jsonObject[this._dataSetName]; + } + } + + // Allow empty object in addRecords with MODE_EMPTY + if (addMode != progress.data.JSDO.MODE_EMPTY) { + if (Object.keys(jsonObject).length === 0) + throw new Error(msg.getMsgText("jsdoMSG006")); + } + + var oldUseRelationships = this.useRelationships; + // Turn off useRelationships since addRecords() does not use the working record + this.useRelationships = false; + try { + for (var buf in this._buffers) { + // Read data for tables in JSON object + if (jsonObject[this._buffers[buf]._name]) + this._addRecords(this._buffers[buf]._name, jsonObject, addMode, + keyFields, trackChanges, isInvoke); + else if (addMode == progress.data.JSDO.MODE_EMPTY) { + this._buffers[this._buffers[buf]._name]._clearData(); + } + } + } finally { + // Restore useRelationships + this.useRelationships = oldUseRelationships; + } + } + else if (this._defaultTableRef) { + this._addRecords(this._defaultTableRef._name, jsonObject, addMode, keyFields, + trackChanges, isInvoke); + } + }; + + /* + * Copies the fields of the source record to the target record. + * Preserves the _id of the target record. + */ + this._copyRecord = function (tableRef, source, target, onlyChangesRecord) { + for (var field in source) { + + if (onlyChangesRecord !== undefined) { + if (source[field] == onlyChangesRecord[field]) + continue; + } + + // Fix for PSC00277769 + if (source[field] === undefined || source[field] === null) { + target[field] = source[field]; + } + else if (source[field] instanceof Date) { + target[field] = source[field]; + } + else if (typeof source[field] === 'object') { + var newObject = source[field] instanceof Array ? [] : {}; + this._copyRecord(tableRef, source[field], newObject); + target[field] = newObject; + } + else + target[field] = source[field]; + } + }; + + /* + * Deletes the "prods:" properties when no longer needed, + * typically when doing acceptChanges, rejectChanges, or _applyChanges. + * These properties are used to transfer before-image info between client JSDO and AppServer. + * + * Also, it optionally clears out the errorString field depending upon value of clearErrorString. + * To be consistent with the handling of + * the ABL's Buffer ERROR-STRING attribute, + * the errorString field should be cleared out when doing acceptChanges() or rejectChanges(). + */ + this._deleteProdsProperties = function (record, clearErrorString, deleteRowState) { + + /* Default to false */ + if (typeof(clearErrorString) == 'undefined') { + clearErrorString = false; + } + + /* Default to true */ + if (typeof(deleteRowState) == 'undefined') { + deleteRowState = true; + } + + if (record) { + delete record["prods:id"]; + delete record["prods:hasErrors"]; + delete record["prods:clientId"]; + delete record["prods:rejected"]; + delete record._rejected; + + if (deleteRowState) { + delete record["prods:rowState"]; + } + + if (clearErrorString) { + delete record._errorString; + } + } + }; + + this._addRecords = function (tableName, jsonObject, addMode, keyFields, trackChanges, isInvoke) { + var beforeImageJsonObject = null; + var beforeImageJsonIndex = null; + + if (jsonObject && (this._dataSetName !== undefined)) { + if (jsonObject[this._dataSetName] && + jsonObject[this._dataSetName]["prods:hasChanges"]) { + beforeImageJsonObject = jsonObject; + beforeImageJsonIndex = {}; + } + else if (jsonObject["prods:hasChanges"]) { + beforeImageJsonObject = {}; + beforeImageJsonObject[this._dataSetName] = jsonObject; + beforeImageJsonIndex = {}; + } + } + + if (typeof(tableName) != 'string') + throw new Error(msg.getMsgText("jsdoMSG020")); + if (!addMode) + throw new Error(msg.getMsgText("jsdoMSG021")); + + switch (addMode) { + case progress.data.JSDO.MODE_APPEND: + case progress.data.JSDO.MODE_EMPTY: + case progress.data.JSDO.MODE_MERGE: + case progress.data.JSDO.MODE_REPLACE: + break; + default: + throw new Error(msg.getMsgText("jsdoMSG022")); + } + + if (!keyFields) + keyFields = []; + else { + if (!(keyFields instanceof Array) && (typeof(keyFields) == 'object')) { + if (keyFields[tableName]) { + keyFields = keyFields[tableName]; + } + else { + keyFields = []; + } + } + } + + if (!(keyFields instanceof Array)) { + throw new Error(msg.getMsgText("jsdoMSG008")); + } + + // Check that the specified field names are in the schema + if (this._buffers[tableName]._fields) { + for (var i = 0; i < keyFields.length; i++) { + var field = this._buffers[tableName]._fields[keyFields[i].toLowerCase()]; + if (field === undefined) { + throw new Error(msg.getMsgText("jsdoMSG009", keyFields[i])); + } + else { + keyFields[i] = field.name; + } + } + } + + trackChanges = trackChanges ? true : false; + + if (tableName) { + if (!(jsonObject instanceof Array)) { + var data = null; + + if (jsonObject === undefined || jsonObject === null) { + jsonObject = {}; + } + + if (this.isDataSet()) { + if (jsonObject[this._dataSetName]) + data = jsonObject[this._dataSetName][tableName]; + else if (jsonObject[tableName]) + data = jsonObject[tableName]; + } else { + if (this._dataProperty) + data = jsonObject[this._dataProperty]; + else if (jsonObject.data) + data = jsonObject.data; + } + + + if (data instanceof Array) { + saveJsonObject = jsonObject; + jsonObject = data; + } + else if ((addMode == progress.data.JSDO.MODE_EMPTY) + && (typeof (jsonObject) == 'object') + && (Object.keys(jsonObject).length === 0)) { + jsonObject = []; // Allow empty object in addRecords with + // MODE_EMPTY + } + // Allow empty object when called by restoreChangesOnlyForTable() + // where there are only deletes - in bi data + else if ((addMode == progress.data.JSDO.MODE_REPLACE) + && (typeof (jsonObject) == 'object') + && (beforeImageJsonObject)) { + jsonObject = []; + } + } + + if (!(jsonObject instanceof Array)) { + throw new Error(msg.getMsgText("jsdoMSG005", tableName)); + } + + var dataHasBeenProcessed = false; + try { + this._buffers[tableName]._sortRecords = false; + if (keyFields.length === 0 || addMode == progress.data.JSDO.MODE_EMPTY) { + // Quick merge + if (addMode == progress.data.JSDO.MODE_EMPTY) { + this._buffers[tableName]._clearData(); + } + // APPEND, MERGE, REPLACE + for (var i = 0; i < jsonObject.length; i++) { + var jsrecord = this._buffers[tableName]._add(jsonObject[i], trackChanges, false); + jsonObject[i]._id = jsrecord.data._id; + if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { + beforeImageJsonIndex[jsonObject[i]["prods:id"]] = jsrecord.data._id; + } + if (beforeImageJsonObject) { + this._deleteProdsProperties(jsrecord.data); + } + } + } + else { + // Build temporary index + var tmpIndex; + + if (this._buffers[tableName]._data.length * jsonObject.length >= 10) { + tmpIndex = {}; + + for (var i = 0; i < this._buffers[tableName]._data.length; i++) { + var record = this._buffers[tableName]._data[i]; + if (!record) continue; + + var key = this._buffers[tableName]._getKey(record, keyFields); + tmpIndex[key] = record; + } + + } + else + tmpIndex = null; // Do not use an index + var checkBeforeImage = + (Object.keys(this._buffers[tableName]._beforeImage).length !== 0); + for (var i = 0; i < jsonObject.length; i++) { + var match = false; + var record = null; + + // Check for duplicates + if (tmpIndex) { + var key = this._buffers[tableName]._getKey(jsonObject[i], keyFields); + record = tmpIndex[key]; + match = (record !== undefined); + } + else { + for (var j = 0; j < this._buffers[tableName]._data.length; j++) { + record = this._buffers[tableName]._data[j]; + if (!record) continue; + match = + (this._buffers[tableName]._equalRecord(jsonObject[i], record, keyFields)); + if (match) { + // Duplicate found + break; + } + } + } + + if (match) { + if (isInvoke + && (this._resource.idProperty !== undefined) + && (jsonObject[i]._id === undefined)) { + // Add _id to jsonObject + jsonObject[i]._id = record._id; + } + + // If beforeRecord is null, there is entry in _beforeImage for a create. + // If beforeRecord is undefined, there is no entry + var beforeRecord = this._buffers[tableName]._beforeImage[record._id]; + if (checkBeforeImage + && (jsonObject[i]["prods:id"] !== undefined) + && (typeof beforeRecord !== 'undefined')) { + // Only throw exception if the existing bi data + // is not the same as the new bi data + var isAfterSame = this._sameData(jsonObject[i], record); + var isBeforeSame = true; + + // For creates, beforeRecord will be null + if (beforeRecord) { + var beforeObject = this._getBeforeRecordFromObject(jsonObject[i], + beforeImageJsonObject, tableName); + if (beforeObject) + isBeforeSame = this._sameData(beforeObject, beforeRecord); + } + + if (!isAfterSame || !isBeforeSame) + throw new Error(msg.getMsgText("jsdoMSG032")); + } + + switch (addMode) { + case progress.data.JSDO.MODE_APPEND: + throw new Error(msg.getMsgText("jsdoMSG023")); + case progress.data.JSDO.MODE_MERGE: + /* Ignore duplicate */ + if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { + beforeImageJsonIndex[jsonObject[i]["prods:id"]] = record._id; + } + break; + case progress.data.JSDO.MODE_REPLACE: + if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { + beforeImageJsonIndex[jsonObject[i]["prods:id"]] = record._id; + } + + if (jsonObject[i]._id === undefined) + jsonObject[i]._id = record._id; + this._copyRecord( + this._buffers[tableName], + jsonObject[i], record); + this._deleteProdsProperties(record); + break; + default: + break; + } + } + else { + // Add record + var jsrecord = + this._buffers[tableName]._add(jsonObject[i], trackChanges, false); + jsonObject[i]._id = jsrecord.data._id; + if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { + beforeImageJsonIndex[jsonObject[i]["prods:id"]] = jsrecord.data._id; + } + if (beforeImageJsonObject) { + this._deleteProdsProperties(jsrecord.data); + } + if (tmpIndex) { + var key = this._buffers[tableName]._getKey(jsrecord.data, keyFields); + tmpIndex[key] = jsrecord.data; + } + } + + } + tmpIndex = null; + } + dataHasBeenProcessed = true; + } + finally { + this._buffers[tableName]._sortRecords = true; + this._buffers[tableName]._sort(); + this._buffers[tableName]._createIndex(); + + if (dataHasBeenProcessed && beforeImageJsonObject) { + this._buffers[tableName]._loadBeforeImageData(beforeImageJsonObject, + beforeImageJsonIndex, keyFields); + } + } + } + }; + + // This method returns corresponding bi record of the afterRecord from specified jsonObject + this._getBeforeRecordFromObject = function (afterRecord, jsonObject, tablename) { + var beforeData = jsonObject[this._dataSetName]["prods:before"]; + var id = afterRecord["prods:id"]; + var beforeRecord; + + if (!beforeData) return beforeRecord; + + // First check to see if the before data is the same + for (var i = 0; i < beforeData[tablename].length; i++) { + var record = beforeData[tablename][i]; + if (record["prods:id"] && id == record["prods:id"]) { + beforeRecord = record; + break; + } + } + + return beforeRecord; + }; + + this._sameData = function (record1, record2) { + var value1, value2; + for (var fieldName in record1) { + if (fieldName.substring(0, 5) != "prods" && fieldName != "_id") { + value1 = record1[fieldName]; + value2 = record2[fieldName]; + + if (value1 > value2 || value1 === null) + return false; + else if (value1 < value2 || value2 === null) + return false; + } + } + + return true; + }; + + + // private method to merge changes after a read operation + this._mergeRead = function (jsonObject, xhr) { + if (this.isDataSet()) { + if (this._dataProperty) { + var datasetBuffer = this._buffers[this._dataProperty]; + datasetBuffer._data = jsonObject[this._dataSetName][this._dataProperty]; + if (datasetBuffer.autoSort) { + datasetBuffer._sort(); + } + datasetBuffer._createIndex(); + } + else { + // Load data from JSON object into _data + for (var buf in this._buffers) { + var data; + if (jsonObject[this._dataSetName]) + data = jsonObject[this._dataSetName][buf]; + else + data = null; + data = data ? data : []; + this._buffers[buf]._data = data; + if (this._buffers[buf].autoSort) { + this._buffers[buf]._sort(); + } + this._buffers[buf]._createIndex(); + if (jsonObject[this._dataSetName] + && jsonObject[this._dataSetName]["prods:hasChanges"]) { + this._buffers[buf]._loadBeforeImageData(jsonObject); + } + } + // Load nested data into _data + if (this._numBuffers > 1) { + for (var buf in this._buffers) { + if (this._buffers[buf]._isNested + && this._buffers[buf]._parent + && this._buffers[this._buffers[buf]._parent]) { + var srcData = this._buffers[this._buffers[buf]._parent]._data; + var data = []; + for (var i = 0; i < srcData.length; i++) { + if (srcData[i][buf] !== undefined) { + for (var j = 0; j < srcData[i][buf].length; j++) { + data.push(srcData[i][buf][j]); + } + delete srcData[i][buf]; + } + } + this._buffers[buf]._data = data; + if (this._buffers[buf].autoSort) { + this._buffers[buf]._sort(); + } + this._buffers[buf]._createIndex(); + } + } + } + } + } + else { + if (jsonObject instanceof Array) { + this._defaultTableRef._data = jsonObject; + } + else { + if (this._dataProperty) + this._defaultTableRef._data = jsonObject[this._dataProperty]; + else if (jsonObject.data) + this._defaultTableRef._data = jsonObject.data; + else { + this._defaultTableRef._data = []; + this._defaultTableRef._data[0] = jsonObject; + } + } + } + + for (var buf in this._buffers) { + if (this._buffers[buf].autoSort) { + this._buffers[buf]._sort(); + } + this._buffers[buf]._createIndex(); + } + }; + + /** + * Replace existing record data and index entry with new record data. + */ + this._mergeUpdateRecord = function (tableRef, recordId, record) { + var index = tableRef._index[recordId].index; + record._id = recordId; + + if (!tableRef._data[index]) { + tableRef._data[index] = {}; + } + this._copyRecord(this._tableRef, record, tableRef._data[index]); + record = tableRef._data[index]; + + if (tableRef._jsdo._resource.idProperty !== undefined) { + var id = tableRef._data[index][tableRef._jsdo._resource.idProperty]; + if (id !== undefined) { + delete tableRef._index[recordId]; + id += ""; + tableRef._index[id] = new progress.data.JSIndexEntry(index); + record._id = id; + } + } + + return record; + }; + + + /** + *update existing record data with specified error string + */ + this._setErrorString = function (tableRef, recordId, errorString, setInBeforeTable) { + + if (setInBeforeTable) { + // Ensure that object exists, it's null for deleted rows + if (tableRef._beforeImage[recordId]) { + tableRef._beforeImage[recordId]._errorString = errorString; + } + } + else { + var index = tableRef._index[recordId].index; + tableRef._data[index]._errorString = errorString; + } + }; + + /* + * Returns the array with the data from the specified dataObject. + */ + this._arrayFromDataObject = function (dataObject, tableRef) { + var data; + + if (dataObject === undefined) return undefined; + if (this._dataSetName) { + if (dataObject[this._dataSetName]) + data = dataObject[this._dataSetName][tableRef._name]; + } + else { + // check if data returned as array + if (dataObject instanceof Array) { + data = dataObject; + } else { + // or if data property is set + if (this._dataProperty) { + data = dataObject[this._dataProperty]; + } else if (dataObject.data) { + // or just try with 'data' as the data property name + data = dataObject.data; + } + } + } + + return data; + }; + + ///////////////////////////////////////////////////////////////////////////////////////////// + // + // Private method to merge changes after a create or update operation. + // This method is called to merge changes when server's Create or Update methods were called. + // + // It returns true if it found error for row in before-image data (prods:hasErrors = true) + // It returns false if there is no before-image data or prods:hasErrors property is absent + this._mergeUpdateForCUD = function (jsonObject, xhr) { + var hasError = false, + errorString; + + // Update dataset with changes from server + if (this._dataSetName) { + var dataSetJsonObject = jsonObject[this._dataSetName]; + + // only updates the specified record + var tableRef = xhr.request.jsrecord._tableRef; + var tableJsonObject = this._arrayFromDataObject(jsonObject, tableRef); + + if (tableJsonObject instanceof Array) { + if (tableJsonObject.length > 1) { + xhr.request.success = false; + throw new Error(msg.getMsgText("jsdoMSG100")); + } + + for (var i = 0; i < tableJsonObject.length; i++) { + var recordId = xhr.request.jsrecord.getId(); + + if (!recordId) { + throw new Error(msg.getMsgText("jsdoMSG034", "_mergeUpdateForCUD()")); + } + + // Determine if error string (get prods_id before _mergeUpdateRecord() is called, + // since it removes all prods properties) + errorString = undefined; + + if (tableJsonObject[i]["prods:hasErrors"]) { + var prods_id = tableJsonObject[i]["prods:id"]; + errorString = + this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); + hasError = true; + } + + var record = this._mergeUpdateRecord(tableRef, recordId, tableJsonObject[i]); + if (errorString) + this._setErrorString(tableRef, recordId, errorString, false); + + // Set _rejected property + if (tableJsonObject[i]["prods:rejected"] + || errorString) { + record._rejected = true; + if (errorString === "REJECTED") { + delete record._errorString; + } + } + + xhr.request.jsrecord = new progress.data.JSRecord(tableRef, record); + } + } + } else { + // update single record with changes from server + var tableRef = this._defaultTableRef; + var data = this._arrayFromDataObject(jsonObject); + + if (data instanceof Array) { + if (data.length > 1) { + xhr.request.success = false; + throw new Error(msg.getMsgText("jsdoMSG100")); + } + + for (var i = 0; i < data.length; i++) { + var recordId = xhr.request.jsrecord.getId(); + + if (!recordId) { + throw new Error(msg.getMsgText("jsdoMSG034", "_mergeUpdateForCUD()")); + } + + var record = this._mergeUpdateRecord(tableRef, recordId, data[i]); + xhr.request.jsrecord = new progress.data.JSRecord(tableRef, record); + } + } + } + + return hasError; + }; + + + ///////////////////////////////////////////////////////////////////////////////////////////// + // + // Private method to determine if deleted row (from delete operation) returned from AppServer + // was returned with an error in the before-image data. + // + // It returns true if it found an error for row in before-image data (prods:hasErrors = true) + // It returns false if there is no before-image data or prods:hasErrors property is absent + + this._checkForDeleteError = function (dataSetJsonObject, xhr) { + var hasError = false; + var tableRef = xhr.request.jsrecord._tableRef; + + beforeJsonObject = dataSetJsonObject["prods:before"]; + + // No merge is necessary for deletes, but we need to see + // if there are any errors on deletes records. + // delete records are not in after table, only in before table + if (beforeJsonObject) { + var beforeTableJsonObject = beforeJsonObject[tableRef._name]; + + if (beforeTableJsonObject.length > 1) { + xhr.request.success = false; + throw new Error(msg.getMsgText("jsdoMSG100")); + } + // clientId is same as _id + var recordId = beforeTableJsonObject[0]["prods:clientId"]; + if (!recordId) { + throw new Error(msg.getMsgText("jsdoMSG035", "_checkForDeleteError()")); + } + + // Determine if row was returned with error string + if (beforeTableJsonObject[0]["prods:hasErrors"]) { + var prods_id = beforeTableJsonObject[0]["prods:id"]; + var errorString = + this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); + this._setErrorString(tableRef, recordId, errorString, true); + hasError = true; + } + } + + return hasError; + }; + + ///////////////////////////////////////////////////////////////////////////////////////////// + // + // Private method to merge changes after a call to saveChanges. + // This method is called when saveChanges(useSubmit) was called with useSubmit=true. + // This can process/merge one or more created, deleted or updated records. + // In order for a jsonObject to have before-image data, it must be associated with a dataset. + // + // It only merges changes in the after table. But we need to look at before-image table to see + // if there were any errors passed back for the deletes + // + this._mergeUpdateForSubmit = function (jsonObject, xhr) { + var errorString; + + //if (!this._dataSetName || !jsonObject[this._dataSetName]["prods:hasChanges"]) + if (!this._dataSetName) { + // "_mergeUpdateForSubmit() can only be called for a dataset" + throw new Error(msg.getMsgText("jsdoMSG036", "_mergeUpdateForSubmit()")); + } + + // response is sent back with extra dataset object wrapper + var dataSetJsonObject = jsonObject[this._dataSetName]; + if (dataSetJsonObject[this._dataSetName]) + dataSetJsonObject = dataSetJsonObject[this._dataSetName]; + + var beforeJsonObject = dataSetJsonObject["prods:before"]; + + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + + var tableJsonObject = dataSetJsonObject[tableRef._name]; + if (tableJsonObject instanceof Array) { + for (var i = 0; i < tableJsonObject.length; i++) { + + var recordId = tableJsonObject[i]["prods:clientId"]; + if (!recordId) { + throw new Error(msg.getMsgText("jsdoMSG035", "_mergeUpdateForSubmit()")); + } + + // Determine if error string (get prods_id before _mergeUpdateRecord() is called, + // since it removes all prods properties) + errorString = undefined; + + if (tableJsonObject[i]["prods:hasErrors"]) { + var prods_id = tableJsonObject[i]["prods:id"]; + errorString = + this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); + } + var record = this._mergeUpdateRecord(tableRef, recordId, tableJsonObject[i]); + if (errorString) { + this._setErrorString(tableRef, recordId, errorString, false); + } + + // Set _rejected property so it can be checked in applyChanges() + if (tableJsonObject[i]["prods:rejected"] + || errorString) { + record._rejected = true; + if (errorString === "REJECTED") { + delete record._errorString; + } + } + + // Now need to update jsrecords. + // We use this data when we fire create, update and delete events. + // Updating so that it contains latest data (data sent back from server) + var jsrecords = xhr.request.jsrecords; + for (var idx = 0; idx < jsrecords.length; idx++) { + if (jsrecords[idx].data["_id"] == recordId) { + jsrecords[idx].data = record; + break; + } + } + } + } + } + + // No merge is necessary for deletes, + // but we need to see if there are any errors on deletes records. + // delete records are not in after table, only in before table + if (beforeJsonObject) { + for (var buf in this._buffers) { + var tableRef = this._buffers[buf]; + var beforeTableJsonObject = beforeJsonObject[tableRef._name]; + var errorString; + + if (beforeTableJsonObject instanceof Array) { + for (var i = 0; i < beforeTableJsonObject.length; i++) { + + if (beforeTableJsonObject[i]["prods:rowState"] == "deleted") { + var recordId = beforeTableJsonObject[i]["prods:clientId"]; + if (!recordId) { + throw new Error(msg.getMsgText("jsdoMSG035", "_mergeUpdateForSubmit()")); + } + + errorString = undefined; + // If row was returned with error string, just copy that over to jsdo record + if (beforeTableJsonObject[i]["prods:hasErrors"]) { + var prods_id = beforeTableJsonObject[i]["prods:id"]; + + errorString = this._getErrorStringFromJsonObject(dataSetJsonObject, + tableRef, prods_id); + this._setErrorString(tableRef, recordId, errorString, true); + } + + // Set _rejected property so it can be checked in applyChanges() + if ((beforeTableJsonObject[i]["prods:rejected"] + || errorString) + && tableRef._beforeImage[recordId]) { + tableRef._beforeImage[recordId]._rejected = true; + if (errorString === "REJECTED") { + delete tableRef._beforeImage[recordId]._errorString; + } + } + } + } + } + } + } + }; + + ///////////////////////////////////////////////////////////////////////////////////////////// + // + // Private method that fires afterCreate, afterUpdate and afterDelete (CUD) triggers after + // saveChanges(true) is called. We must fire create, update and delete triggers + // for each record that was sent to backend submit operation + this._fireCUDTriggersForSubmit = function (request) { + // Before firing triggers, delete prods properties (except rowState) so they don't appear in data + for (var idx = 0; idx < request.jsrecords.length; idx++) { + this._deleteProdsProperties(request.jsrecords[idx].data, false, false); + } + + for (var idx = 0; idx < request.jsrecords.length; idx++) { + var jsrecord = request.jsrecords[idx]; + switch (jsrecord.data["prods:rowState"]) { + case "created": + jsrecord._tableRef.trigger("afterCreate", this, jsrecord, request.success, request); + this.trigger("afterCreate", this, jsrecord, request.success, request); + break; + case "modified": + jsrecord._tableRef.trigger("afterUpdate", this, jsrecord, request.success, request); + this.trigger("afterUpdate", this, jsrecord, request.success, request); + break; + case "deleted": + jsrecord._tableRef.trigger("afterDelete", this, jsrecord, request.success, request); + this.trigger("afterDelete", this, jsrecord, request.success, request); + break; + } + } + }; + + ////////////////////////////////////////////////////////////////////////////////////////////// + // + // Private method to return error for specified row + // from jsonObject's prods:errors object (before-data) sent over from AppServer + // + this._getErrorStringFromJsonObject = function (dataSetJsonObject, tableRef, prods_id) { + var tableJsonObject; + var errorsJsonObject = dataSetJsonObject["prods:errors"]; + + if (errorsJsonObject) { + tableJsonObject = errorsJsonObject[tableRef._name]; + } + + if (tableJsonObject instanceof Array) { + for (var i = 0; i < tableJsonObject.length; i++) { + + var id = tableJsonObject[i]["prods:id"]; + if (id === prods_id) { + var errorString = tableJsonObject[i]["prods:error"]; + return errorString === null ? + "Server returned unspecified error. Please check log files." : errorString; + } + } + } + + return undefined; + }; + + this._fillSuccess = function (jsdo, success, request) { + var xhr = request.xhr, + properties; + + // Need to check if responseMapping was specified; developer can specify + // plug-in to manipulate response + properties = jsdo.getMethodProperties("read"); + + if (properties && properties.mappingType) { + mapping = progress.data.PluginManager.getPlugin(properties.mappingType); + if (!mapping) { + throw new Error(progress.data._getMsgText("jsdoMSG118", properties.mappingType)); + } + + if (typeof (mapping.responseMapping) === "function") { + request.response = mapping.responseMapping(jsdo, request.response, { operation: "read" }); + } + } + + jsdo._clearData(); + jsdo._mergeRead(request.response, xhr); + + // Set working record + for (var buf in jsdo._buffers) { + if (!jsdo._buffers[buf]._parent || !jsdo.useRelationships) { + jsdo._buffers[buf]._setRecord(jsdo._buffers[buf]._findFirst()); + } + } + }; + + this._fillComplete = function (jsdo, success, request) { + jsdo.trigger("afterFill", jsdo, request.success, request); + if (request.deferred) { + if (success) { + request.deferred.resolve(jsdo, success, request); + } + else { + request.deferred.reject(jsdo, success, request); + } + } + }; + + this._fillError = function (jsdo, success, request) { + jsdo._clearData(); + jsdo._updateLastErrors(jsdo, null, null, request); + }; + + this._undoCreate = function (tableRef, id) { + // Undo operation + // Remove record from JSDO memory + var entry = tableRef._index[id]; + if (entry !== undefined) { + var index = entry.index; + tableRef._data[index] = null; + } + tableRef._hasEmptyBlocks = true; + delete tableRef._index[id]; + delete tableRef._beforeImage[id]; + // End - Undo operation + }; + + this._undoUpdate = function (tableRef, id, deleteProdsProps) { + /* Default to false */ + if (typeof(deleteProdsProps) == 'undefined') { + deleteProdsProps = false; + } + + // Undo operation + // Restore from before image + var record = tableRef._beforeImage[id]; + + // Before image points to an existing record + if (record) { + var index = tableRef._index[id].index; + tableRef._jsdo._copyRecord(tableRef, record, tableRef._data[index]); + if (deleteProdsProps) + tableRef._jsdo._deleteProdsProperties(tableRef._data[index], true); + } + delete tableRef._beforeImage[id]; + // End - Restore before image + }; + + this._undoDelete = function (tableRef, id, deleteProdsProps) { + /* Default to false */ + if (typeof(deleteProdsProps) == 'undefined') { + deleteProdsProps = false; + } + + // Restore from before image + var record = tableRef._beforeImage[id]; + + // Before image points to an existing record + if (record) { + var index = record._index; + delete record._index; + if (deleteProdsProps) + tableRef._jsdo._deleteProdsProperties(record, true); + + if ((index !== undefined) && (tableRef._data[index] === null)) { + tableRef._data[index] = record; + } + else { + tableRef._data.push(record); + index = tableRef._data.length - 1; + } + tableRef._index[id] = new progress.data.JSIndexEntry(index); + } + delete tableRef._beforeImage[id]; + // End - Restore before image + }; + + this._deleteComplete = function (jsdo, success, request) { + var xhr = request.xhr; + var jsrecord = request.jsrecord; + + try { + // Before firing trigger, delete prods properties so they don't appear in data + jsdo._deleteProdsProperties(jsrecord.data, false); + + jsrecord._tableRef.trigger("afterDelete", jsdo, jsrecord, request.success, request); + jsdo.trigger("afterDelete", jsdo, jsrecord, request.success, request); + + } finally { + request.complete = true; + jsdo._checkSaveComplete(xhr); + } + }; + + this._deleteSuccess = function (jsdo, success, request) { + var xhr = request.xhr; + var jsonObject = request.response; + var beforeJsonObject = null; + var dataSetJsonObject = null; + var data; + + //Even though this is _deleteSuccess, if before-image data is returned, the call of + // delete operation could return a success, but we have to check if error was returned + // in before-image data + var hasError = false; + if (jsdo._useBeforeImage("delete")) { + dataSetJsonObject = jsonObject[jsdo._dataSetName]; + beforeJsonObject = dataSetJsonObject["prods:before"]; + + if (beforeJsonObject) { + data = beforeJsonObject[request.jsrecord._tableRef._name]; + } + } + else { + data = jsdo._arrayFromDataObject(jsonObject, request.jsrecord._tableRef); + } + + if (data instanceof Array) { + if (data.length > 1) { + request.success = false; + throw new Error(msg.getMsgText("jsdoMSG100")); + } + } + + if (beforeJsonObject) { + hasError = jsdo._checkForDeleteError(dataSetJsonObject, xhr); + } + + if (hasError) + request.success = false; + + if (jsdo.autoApplyChanges) { + if (!hasError) { + // Clear before image + delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; + // End - Clear before image + } + else { + jsdo._deleteError(jsdo, success, request); + } + } + }; + + this._deleteError = function (jsdo, success, request) { + if (jsdo.autoApplyChanges) { + jsdo._undoDelete(request.jsrecord._tableRef, request.jsrecord.data._id); + } + }; + + this._createComplete = function (jsdo, success, request) { + var xhr = request.xhr; + var jsrecord = request.jsrecord; + + try { + // Before firing trigger, delete prods properties so they don't appear in data + jsdo._deleteProdsProperties(jsrecord.data, false); + + jsrecord._tableRef.trigger("afterCreate", jsdo, jsrecord, request.success, request); + jsdo.trigger("afterCreate", jsdo, jsrecord, request.success, request); + } finally { + request.complete = true; + jsdo._checkSaveComplete(xhr); + } + }; + + this._createSuccess = function (jsdo, success, request) { + var xhr = request.xhr; + var record = request.response; + var hasError = jsdo._mergeUpdateForCUD(record, xhr); + + if (hasError) + request.success = false; + + if (jsdo.autoApplyChanges) { + if (!hasError) { + // Clear before image + delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; + // End - Clear before image + } + else { + jsdo._createError(jsdo, success, request); + } + } + }; + + this._createError = function (jsdo, success, request) { + if (jsdo.autoApplyChanges) { + jsdo._undoCreate(request.jsrecord._tableRef, request.jsrecord.data._id); + } + }; + + + this._updateComplete = function (jsdo, success, request) { + var xhr = request.xhr; + var jsrecord = request.jsrecord; + try { + // Before firing trigger, delete prods properties so they don't appear in data + jsdo._deleteProdsProperties(jsrecord.data, false); + + jsrecord._tableRef.trigger("afterUpdate", jsdo, jsrecord, request.success, request); + jsdo.trigger("afterUpdate", jsdo, jsrecord, request.success, request); + } finally { + request.complete = true; + jsdo._checkSaveComplete(xhr); + } + }; + + this._updateSuccess = function (jsdo, success, request) { + var xhr = request.xhr; + var hasError = jsdo._mergeUpdateForCUD(request.response, xhr); + + if (hasError) { + request.success = false; + } + + if (jsdo.autoApplyChanges) { + if (!hasError) { + request.success = true; + // Clear before image + delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; + // End - Clear before image + } + else { + jsdo._updateError(jsdo, success, request); + } + } + }; + + this._updateError = function (jsdo, success, request) { + + if (jsdo.autoApplyChanges) { + request.success = false; + jsdo._undoUpdate(request.jsrecord._tableRef, request.jsrecord.data._id); + } + }; + + + this._saveChangesSuccess = function (jsdo, success, request) { + var records = request.response; + jsdo._mergeUpdateForSubmit(records, request.xhr); + + // Ensure that that the _lastErrors variable has been cleared + jsdo._clearErrors(); + var changes = jsdo.getChanges(); + jsdo._updateLastErrors(jsdo, null, changes); + + jsdo._setAllRecordsRejected(changes); + + if (jsdo.autoApplyChanges) { + jsdo._applyChanges(); + } + }; + + + this._saveChangesError = function (jsdo, success, request) { + jsdo._setAllRecordsRejected(true); + if (jsdo.autoApplyChanges) { + jsdo.rejectChanges(); + } + jsdo._updateLastErrors(jsdo, null, null, request); + }; + + /* _saveChangesSuccessTT + internal function called after a Submit of a temp-table (not DataSet) returns success + This method does not attempt to do any merging of records into the JSDO memory. The + absence of _id for the records means that the only way we could possibly do a "merge" + would be to delete the changed rceords in the JSDO memory and then add the records + that were returned form the data service, but that would invalidate the _id's that + the Kendo datasource depends on. The application programmmer must do the merging in + the afterSaveChanges handler + + *** Submit(temp-table) is not supported. This method will be removed in a future version. *** + */ + this._saveChangesSuccessTT = function (jsdo, success, request) { + var changes; + + // Ensure that that the _lastErrors variable has been cleared + jsdo._clearErrors(); + changes = jsdo.getChanges(); + jsdo._updateLastErrors(jsdo, null, changes); + jsdo._setAllRecordsRejected(false); + }; + + this._saveChangesComplete = function (jsdo, success, request) { + // Success with errors + if ((request.xhr.status >= 200 && request.xhr.status < 300) + && (jsdo._lastErrors.length > 0 || jsdo._someRecordsRejected)) { + request.success = false; + } + + // If saveChanges(true) was called, then we must fire create, update and delete triggers + // for each record that was sent to submit operation + if (jsdo._useSubmit === true) { + jsdo._fireCUDTriggersForSubmit(request); + } + + jsdo._undefWorkingRecord(); + jsdo._fireAfterSaveChanges(request.success, request); + + }; + + this._fireAfterSaveChanges = function (success, request) { + this.trigger("afterSaveChanges", this, success, request); + + if (request.jsrecords) { + if (request.deferred) { + if (success) { + request.deferred.resolve(this, success, request); + } + else { + request.deferred.reject(this, success, request); + } + } + } + else if (request.batch && request.batch.deferred) { + if (success) { + request.batch.deferred.resolve(this, success, request); + } + else { + request.batch.deferred.reject(this, success, request); + } + } + + // Clear error string when autoApplyChanges is true + var clearErrorString = this.autoApplyChanges; + + // This will be set if submit operation was performed + if (request.jsrecords) { + for (var idx = 0; idx < request.jsrecords.length; idx++) { + var jsrecord = request.jsrecords[idx]; + if (clearErrorString) { + delete jsrecord.data._errorString; + } + delete jsrecord.data["prods:rowState"]; + } + } + else if (request.batch && request.batch.operations) { + for (var idx = 0; idx < request.batch.operations.length; idx++) { + var jsrecord = request.batch.operations[idx].jsrecord; + if (clearErrorString) { + delete jsrecord.data._errorString; + } + } + } + }; + + /* + * Returns errors in response associated with the HTTP request.records related to the specified jsrecord. + */ + this._getErrorsFromRequest = function(request) { + var errors = [], // Array of objects with properties: type, id, error, errorNum, responseText + errorArray = [], + errorObject, + retValString, + j, + i; + + if (request && !request.success) { + if (request.xhr.status >= 400 && request.xhr.status < 600) { + try { + responseObject = JSON.parse(request.xhr.responseText); + + // responseText could be an array, an object or just text. + // If it is an array, each object would have properties _errors and optional _retVal. + // If it is not an array, the object would have properties _errors and optional _retVal. + // If it is text, the content could also be an HTML page, this error is handle using "HTTP Status". + if (responseObject instanceof Array) { + errorArray = responseObject; + } else if (responseObject instanceof Object) { + errorArray.push(responseObject); + } + for (i = 0; i < errorArray.length; i += 1) { + errorObject = errorArray[i]; + if (errorObject._retVal) { + errors.push({ + type: progress.data.JSDO.RETVAL, + error: errorObject._retVal + }); + retValString = errorObject._retVal; + } else { + retValString = null; + } + if (errorObject._errors instanceof Array) { + for (j = 0; j < errorObject._errors.length; j += 1) { + if ((errorObject._errors[j]._errorNum === 0) + && (errorObject._errors[j]._errorMsg === retValString)) { + // Suppress additional error msg if it is same as return value + continue; + } + errors.push({ + type: progress.data.JSDO.APP_ERROR, + error: errorObject._errors[j]._errorMsg, + errorNum: errorObject._errors[j]._errorNum + }); + } + } + } + } + catch (e) { + // Ignore exceptions + } + } + if (request.exception) { + errors.push({ + type: progress.data.JSDO.ERROR, + error: request.exception + }); + } + if (errors.length === 0 + && request.xhr + && (request.xhr.status >= 400 && request.xhr.status < 600)) { + errors.push({ + type: progress.data.JSDO.ERROR, + error: "Error: HTTP Status " + request.xhr.status + " " + request.xhr.statusText, + responseText: request.xhr.responseText + }); + } + } + return errors; + }; + + this._updateLastErrors = function (jsdo, batch, changes, request) { + var errors, + errorText, + responseObject, + i, + j, + buf; + + if (batch) { + if (batch.operations === undefined) return; + for (i = 0; i < batch.operations.length; i++) { + request = batch.operations[i]; + if (!request.success && request.xhr) { + if (request.xhr.status >= 200 && request.xhr.status < 300) { + // Add error string to jsdo._lastErrors + jsdo._lastErrors.push({errorString: request.jsrecord.data._errorString}); + // Add error object to jsdo.._lastErrors + jsdo._buffers[request.jsrecord._tableRef._name]._lastErrors.push({ + type: progress.data.JSDO.DATA_ERROR, + id: request.jsrecord.data._id, + error: request.jsrecord.data._errorString}); + } + else { + errors = this._getErrorsFromRequest(request); + errorText = ""; + for (j = 0; j < errors.length; j += 1) { + if (errors.length > 1 && errors[j].error.indexOf("(7243)") != -1) { + // If there are more error messages + // supress error "The Server application has returned an error. (7243)" + continue; + } + // Add error to table reference + if (request.jsrecord + && (errors[j].type === progress.data.JSDO.APP_ERROR + || errors[j].type === progress.data.JSDO.RETVAL)) { + errors[j].id = request.jsrecord.data._id; + request.jsrecord._tableRef._lastErrors.push(errors[j]); + } + if (errorText.length === 0) { + errorText = errors[j].error; + } + else { + errorText += "\n" + errors[j].error; + } + } + // Add error string to jsdo._lastErrors + jsdo._lastErrors.push({errorString: errorText}); + } + } + } + } + else if (changes instanceof Array) { + for (i = 0; i < changes.length; i++) { + if (changes[i].record && changes[i].record.data._errorString !== undefined) { + jsdo._lastErrors.push({errorString: changes[i].record.data._errorString}); + jsdo._buffers[changes[i].record._tableRef._name]._lastErrors.push({ + type: progress.data.JSDO.DATA_ERROR, + id: changes[i].record.data._id, + error: changes[i].record.data._errorString}); + } + } + } + else if (request + && !request.success + && request.xhr + && ((request.xhr.status >= 400 && request.xhr.status < 600) || request.xhr.status === 0)) { + errors = this._getErrorsFromRequest(request); + errorText = ""; + for (j = 0; j < errors.length; j += 1) { + if (errors.length > 1 && errors[j].error.indexOf("(7243)") != -1) { + // If there are more error messages + // supress error "The Server application has returned an error. (7243)" + continue; + } + // Add error to all table references + for (buf in this._buffers) { + this._buffers[buf]._lastErrors.push(errors[j]); + } + if (errorText.length === 0) { + errorText = errors[j].error; + } + else { + errorText += "\n" + errors[j].error; + } + } + jsdo._lastErrors.push({errorString: errorText}); + } + }; + + // Check if all the xhr operations associated with the batch for which + // this xhr object is related have completed (not necessarily to success). + // If all XHR operations have completed this fires 'afterSaveChanges' event + this._checkSaveComplete = function (xhr) { + if (xhr.request) { + var jsdo = xhr.request.jsdo; + var batch = xhr.request.batch; + // OE00229270 Should only do afterSaveChanges if _async + if (jsdo && batch && jsdo._async) { + if (jsdo._isBatchComplete(batch)) { + var success = jsdo._isBatchSuccess(batch); + var request = { + batch: batch, + success: success + }; + jsdo._undefWorkingRecord(); + + // Save error messages + jsdo._lastErrors = []; + if (!success && batch.operations) { + jsdo._updateLastErrors(jsdo, batch, null); + } + this._setAllRecordsRejected(batch); + + jsdo._fireAfterSaveChanges(success, request); + } + } + } + }; + + + /* + * determine if a batch of XHR requests has completed in which all requests are successful + */ + this._isBatchSuccess = function (batch) { + if (batch.operations) { + for (var i = 0; i < batch.operations.length; i++) { + if (!batch.operations[i].success) { + return false; + } + } + } + return true; + }; + + /* + * determine if all XHR requests from the batch of saves have completed (not necessarily to success) + */ + this._isBatchComplete = function (batch) { + if (batch.operations) { + for (var i = 0; i < batch.operations.length; i++) { + var request = batch.operations[i]; + // we have to check against the 'complete' flag because xhr.readyState + // might be set async by the browser + // while we're still in the middle of processing some other requests's response + if (!request.complete) { + return false; + } + } + } + return true; + }; + + this._mergeInvoke = function (jsonObject, xhr) { + var operation; + if (xhr.request.fnName !== undefined + && xhr.jsdo._resource.fn[xhr.request.fnName] !== undefined) { + operation = xhr.jsdo._resource.fn[xhr.request.fnName].operation; + } + else + operation = null; + if (operation === undefined) { + // Operation data is only required for invoke operations with mergeMode: true + operation = null; + for (var i = 0; i < xhr.jsdo._resource.operations.length; i++) { + if (xhr.jsdo._resource.operations[i].name == xhr.request.fnName) { + operation = xhr.jsdo._resource.operations[i]; + break; + } + } + xhr.jsdo._resource.fn[xhr.request.fnName].operation = operation; + } + if (operation !== null && operation.mergeMode) { + try { + var mergeMode = progress.data.JSDO["MODE_" + operation.mergeMode.toUpperCase()]; + if (mergeMode === null) { + throw new Error(msg.getMsgText("jsdoMSG030", "mergeMode property", + "EMPTY, APPEND, MERGE or REPLACE")); + } + if (xhr.jsdo._resource.idProperty === undefined) { + throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, + " by mergeMode property in invoke operation")); + } + var dataParameterName; + if (xhr.jsdo.isDataSet()) { + dataParameterName = xhr.jsdo._resource._dataSetName; + } + else if (xhr.jsdo._resource.dataProperty !== undefined) { + dataParameterName = xhr.jsdo._resource.dataProperty; + } + else if (xhr.jsdo._resource._tempTableName !== undefined) { + dataParameterName = xhr.jsdo._resource._tempTableName; + } + else { + throw new Error(msg.getMsgText("jsdoMSG111", "")); + } + + var found = false; + for (var i = 0; i < operation.params.length; i++) { + if (operation.params[i].name == dataParameterName) { + if (operation.params[i].type.indexOf('RESPONSE_BODY') != -1) { + if ((operation.params[i].xType !== undefined) + && (operation.params[i].xType != 'DATASET') + && (operation.params[i].xType != 'TABLE') + && (operation.params[i].xType != 'ARRAY')) { + throw new Error(msg.getMsgText("jsdoMSG113", operation.params[i].xType, + dataParameterName, xhr.request.fnName)); + } + found = true; + break; + } + } + } + + if (!found) { + throw new Error(msg.getMsgText("jsdoMSG112", dataParameterName, xhr.request.fnName)); + } + xhr.jsdo.addRecords(xhr.request.response[dataParameterName], + mergeMode, [xhr.jsdo._resource.idProperty], false, true); + } + catch (e) { + xhr.request.success = false; + xhr.request.exception = e; + } + } + }; + + this.onReadyStateChangeGeneric = function () { + var xhr = this; + if (xhr.readyState == 4) { + var request = xhr.request; + + /* try to parse response even if request is considered "failed" due to http status */ + try { + request.response = JSON.parse(xhr.responseText); + // in some cases the object back from appserver has a "response" property which represents + // the real content of the JSON...happens when multiple output parameters are returned. + // this of course assumes no one names their root object "response". + if (request.response && request.response.response) { + request.response = request.response.response; + } + } catch (e) { + request.response = undefined; + } + + try { + if ((xhr.status >= 200 && xhr.status < 300) + || (xhr.status === 0 && xhr.responseText !== "")) { + + request.success = true; + // get the Client Context ID (AppServer ID) + xhr.jsdo._session._saveClientContextId(xhr); + if ((typeof xhr.onSuccessFn) == 'function') { + var operation; + if (xhr.request.fnName !== undefined + && xhr.jsdo._resource.fn[xhr.request.fnName] !== undefined) { + operation = xhr.jsdo._resource.fn[xhr.request.fnName].operation; + } + else + operation = null; + if ((operation === undefined) || (operation !== null && operation.mergeMode)) + xhr.jsdo._mergeInvoke(request.response, xhr); + if (request.success) + xhr.onSuccessFn(xhr.jsdo, request.success, request); + else if ((typeof xhr.onErrorFn) == 'function') + xhr.onErrorFn(xhr.jsdo, request.success, request); + } + + } else { + request.success = false; + if (xhr.status === 0) { + request.exception = new Error(msg.getMsgText("jsdoMSG101")); + } + if ((typeof xhr.onErrorFn) == 'function') { + xhr.onErrorFn(xhr.jsdo, request.success, request); + } + } + } catch (e) { + request.success = false; + request.exception = e; + if ((typeof xhr.onErrorFn) == 'function') { + xhr.onErrorFn(xhr.jsdo, request.success, request); + } + } + // get the Client Context ID (AppServer ID) + xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); + + if ((typeof xhr.onCompleteFn) == 'function') { + xhr.onCompleteFn(xhr.jsdo, request.success, request); + } + + } + }; + + /* + * Accepts changes for all table references in the JSDO. + */ + this.acceptChanges = function () { + for (var buf in this._buffers) { + this._buffers[this._buffers[buf]._name].acceptChanges(); + } + }; + + /* + * Rejects changes for the table references in the JSDO. + */ + this.rejectChanges = function () { + for (var buf in this._buffers) { + this._buffers[this._buffers[buf]._name].rejectChanges(); + } + }; + + /* + * Returns an array with changes for all table references in the JSDO. + */ + this.getChanges = function () { + var result = []; + for (var buf in this._buffers) { + var changes = this._buffers[this._buffers[buf]._name].getChanges(); + result = result.concat(changes); + } + return result; + }; + + this.hasChanges = function () { + for (var buf in this._buffers) { + if (this._buffers[this._buffers[buf]._name].hasChanges()) + return true; + } + return false; + }; + + /* + * Private method to apply changes for all table references in the JSDO. + * If _errorString has been set for a row, rejectRowChanges() is called. + * If it has not been set, acceptRowChanges() is called. + */ + this._applyChanges = function () { + for (var buf in this._buffers) { + this._buffers[this._buffers[buf]._name]._applyChanges(); + } + }; + + /* + * Accepts row changes for the working record using the JSDO reference. + */ + this.acceptRowChanges = function () { + if (this._defaultTableRef) + return this._defaultTableRef.acceptRowChanges(); + throw new Error(msg.getMsgText("jsdoMSG001", "acceptRowChanges()")); + }; + + /* + * Reject row changes for the working record using the JSDO reference. + */ + this.rejectRowChanges = function () { + if (this._defaultTableRef) + return this._defaultTableRef.rejectRowChanges(); + throw new Error(msg.getMsgText("jsdoMSG001", "rejectRowChanges()")); + }; + + /* + * Sets complete set of properties for the jsdo. All existing properties are replaced with new set + */ + this.setProperties = function( propertiesObject ) { + var prop; + + if (arguments.length < 1) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'setProperties', 1)); + } + if (arguments.length > 1) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'setProperties', 1)); + } + if ( typeof propertiesObject == "object" ) { + /* Copy properties of the propertiesObject argument into _properties. + * Note that if object passed in has a prototype, this code copies them too) + */ + this._properties = {}; + + for (prop in propertiesObject) { + if( propertiesObject.hasOwnProperty(prop) ) { + if (typeof propertiesObject[prop] !== "function" ) { + this._properties[prop] = propertiesObject[prop]; + } + } + } + } + else if ( (propertiesObject === undefined) || (propertiesObject === null) ) { + this._properties = {}; + } + else { + // {1}: Parameter {1} must be of type {3} in {4} call. + throw new Error(progress.data._getMsgText("jsdoMSG121", 'JSDO', 1, 'Object', + 'setProperties')); + } + }; + + /* + * Set or remove an individual property in the property set maintained by the jsdo. + * This operates only on the property identified by propertyName; + * all other existing properties remain as they are. + * If the propertyName is not part of the context, this call adds it. + * If it exists, it is updated, unless - + * If propertyValue is undefined, this call removes the property + */ + this.setProperty = function( propertyName, propertyValue) { + if (arguments.length < 2) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', + 'setProperty', 2)); + } + if (arguments.length !== 2) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", "JSDO", + "setProperty", 2)); + } + if (typeof propertyName !== "string") { + // {1}: Parameter {1} must be of type {3} in {4} call. + throw new Error(progress.data._getMsgText("jsdoMSG121", 'JSDO', 1, 'string', + 'setProperty')); + } + + if ( propertyValue === undefined ) { + delete this._properties[propertyName]; // OK if it doesn't exist -- no error + } + else { + this._properties[propertyName] = propertyValue; + } + }; + + /* + * Gets the set of jsdo properties. Returns an object containing all the properties + */ + this.getProperties = function( ) { + if (arguments.length > 0) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperties', 0)); + } + return this._properties; + }; + + /* Gets the value of an individual property in the jsdo property set + */ + this.getProperty = function( propertyName) { + if (arguments.length < 1) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperty', 1)); + } + if (arguments.length > 1) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperty', 1)); + } + return this._properties[propertyName]; + + }; + + /////////////////////////////////////////////////////////////////////////// + // + // The following methods provide support for Object Pesistence + + /* + * Saves JSDO memory (and optionally pending changes) to local storage. + * + * saveLocal() + * saveLocal(name) + * saveLocal(dataMode) + * saveLocal(name, dataMode) + * + */ + this.saveLocal = function saveLocal(arg1, arg2) { + var name; + var dataMode; + + if (arguments.length > 2) { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); + } + + if (typeof(arg1) == 'string' || arg1 === null || arg1 === undefined) { + name = arg1; + dataMode = arg2; + } + else { + name = null; + dataMode = arg1; + } + + if (name === undefined || name === null || name === "") { + name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; + } + if (typeof(dataMode) == 'undefined') { + dataMode = progress.data.JSDO.ALL_DATA; + } + else { + switch (dataMode) { + case progress.data.JSDO.ALL_DATA: + case progress.data.JSDO.CHANGES_ONLY: + break; + default: + throw new Error(msg.getMsgText("jsdoMSG115", arguments.callee.name)); + } + } + + if (this._localStorage === null) { + // Must first instantiate _localStorage object + this._localStorage = new progress.data.LocalStorage(); + } + + var dataObj = this._prepareDataObjectForLocalStorage(dataMode); + this._localStorage.saveToLocalStorage(name, dataObj); + }; + + /* + * Reads localStorage (based upon name) into JSDO memory + * (localStorage may or may not have pending changes). + * readLocal() + * readLocal(name) + * + */ + this.readLocal = function readLocal(name) { + if (arguments.length > 1) { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); + } + if (name === undefined || name === null || name === "") { + name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; + } + else if (typeof(name) != 'string') { + throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); + } + + if (this._localStorage === null) { + this._localStorage = new progress.data.LocalStorage(); + } + + var object = this._localStorage.readFromLocalStorage(name); + + // If storage area does not exist (i.e. object = null) then don't update JSDO local memory + if (object) { + if (this._hasMatchingSchema(object) === false) + throw new Error(msg.getMsgText("jsdoMSG117", name)); + + // For readLocal(), JSDO should first be emptied of data, so using MODE_EMPTY + this._restoreFromLocalStorage(object, progress.data.JSDO.MODE_EMPTY); + } + + return object !== null; + }; + + /* + * Reads localStorage (based upon name) into JSDO memory + * (localStorage may or may not have pending changes). + * addLocalRecords(addMode) + * addLocalRecords(addMode, keyFields) + * addLocalRecords(name, addMode) + * addLocalRecords(name, addMode, keyFields) + */ + this.addLocalRecords = function addLocalRecords(arg1, arg2, arg3) { + var name; + var addMode; + var keyFields; + + if (arguments.length < 1) { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); + } + + if (typeof(arg1) == 'string') { + name = arg1; + addMode = arg2; + keyFields = arg3; + } + else { + name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; + addMode = arg1; + keyFields = arg2; + } + + if (typeof(name) == 'undefined' || name === null || name === "") { + name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; + } + else if (typeof(name) != 'string') { + throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); + } + + if (addMode != progress.data.JSDO.MODE_REPLACE) { + throw new Error(msg.getMsgText("jsdoMSG115", arguments.callee.name)); + } + + if (this._localStorage === null) { + this._localStorage = new progress.data.LocalStorage(); + } + + var object = this._localStorage.readFromLocalStorage(name); + + // If storage area does not exist (i.e. object = null) then don't update JSDO local memory + if (object) { + if (this._hasMatchingSchema(object) === false) + throw new Error(msg.getMsgText("jsdoMSG117", name)); + + try { + this._restoreFromLocalStorage(object, addMode, keyFields); + } + catch (e) { + var text = e.message; + throw new Error(text.replace(new RegExp('addRecords', 'g'), 'addLocalRecords')); + } + } + + return object !== null; + }; + + + /* + * This method returns True if each buffer in the jsdo contains a primary key. + */ + this._containsPrimaryKeys = function _containsPrimaryKeys() { + + for (var buf in this._buffers) { + if (this._buffers[buf]._primaryKeys === null) + return false; + } + + return true; + }; + + /* + * Compares JSDO's dataset/table names with those in specified storage object. + * Returns true if they match (or if storageObject is null or empty), else false. + */ + this._hasMatchingSchema = function _hasMatchingSchema(storageObject) { + var isValid = true; + + if (storageObject === null || (Object.keys(storageObject).length === 0)) + return true; + + + if (this._dataSetName) { + if (storageObject[this._dataSetName]) { + for (var buf in this._buffers) + if (storageObject[this._dataSetName][buf] === undefined) { + isValid = false; + break; + } + } + else + isValid = false; // dataset should be in storage area + } + else if (this._dataProperty) { + // If array, we had to wrap in "fake" dataset, so unwrap it + storageObject = storageObject["_localStorage"]; + if (storageObject === undefined || storageObject[this._dataProperty] === undefined) + isValid = false; + } + else { + // If temp-table, we had to wrap in "fake" dataset, so unwrap it + storageObject = storageObject["_localStorage"]; + if (storageObject === undefined || storageObject[this._defaultTableRef._name] === undefined) + isValid = false; + } + + return isValid; + }; + + + /* + * Clears the data saved to local storage. + * + * deleteLocal() + * deleteLocal(name) + */ + this.deleteLocal = function deleteLocal(name) { + if (arguments.length > 1) { + throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); + } + if (name === undefined || name === null || name === "") { + name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; + } + else if (typeof(name) != 'string') { + throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); + } + + if (this._localStorage === null) { + this._localStorage = new progress.data.LocalStorage(); + } + this._localStorage.clearLocalStorage(name); + }; + + + // This method is used by saveLocal() to return a jsonObject with current JSDO data based upon option. + // + // In order to take advantage of existing code (createChangeSet() and addRecords()) and particularly + // to use the processing of before-data in addRecords(), for tables and arrays, we create a dummy + // dataset name: _localStorage. + this._prepareDataObjectForLocalStorage = function (option) { + + var storageObject = {}; + + // DataSets + if (this._dataSetName) { + switch (option) { + case progress.data.JSDO.ALL_DATA: + storageObject = this._createDataAndChangeSet(this._dataSetName); + break; + + case progress.data.JSDO.CHANGES_ONLY: + storageObject = this._createChangeSet(this._dataSetName, true); + break; + } + } + // Arrays + else if (this._dataProperty) { + switch (option) { + case progress.data.JSDO.ALL_DATA: + storageObject = this._createDataAndChangeSet("_localStorage"); + break; + + case progress.data.JSDO.CHANGES_ONLY: + storageObject = this._createChangeSet("_localStorage", true); + break; + } + } + // Temp Tables + else { + switch (option) { + case progress.data.JSDO.ALL_DATA: + storageObject = this._createDataAndChangeSet("_localStorage"); + break; + + case progress.data.JSDO.CHANGES_ONLY: + storageObject = this._createChangeSet("_localStorage", true); + break; + } + } + + return storageObject; + }; + + + // Restore the data retrieved from local storage to the JSDO based upon the specified addMode + this._restoreFromLocalStorage = function (storageObject, addMode, keyFields) { + + if (storageObject && (Object.keys(storageObject).length > 0)) { + if (this._dataSetName) { + // Walk thru all tables to retrieve data + for (var buf in this._buffers) + this._restoreDataForTable(this._buffers[buf], storageObject, addMode, keyFields); + } + // Either temp-table or array + else + this._restoreDataForTable(this._defaultTableRef, storageObject, addMode, keyFields); + } + else if (addMode === progress.data.JSDO.MODE_EMPTY) + this._clearData(); + }; + + + this._restoreDataForTable = function (tableRef, jsonObject, addMode, keyFields) { + + // If primaryKeys not found, check if the idProperty is there + keyFields = keyFields !== undefined ? keyFields : tableRef._primaryKeys; + if (keyFields === undefined && this._resource.idProperty) { + keyFields = []; + keyFields[0] = this._resource.idProperty; + } + + if (this._dataSetName) { + var oldUseRelationships = this.useRelationships; + // Turn off useRelationships since addRecords() does not use the working record + this.useRelationships = false; + + try { + tableRef.addRecords(jsonObject, addMode, keyFields); + } finally { + // Restore useRelationships + this.useRelationships = oldUseRelationships; + } + } + // else it's either an array (this._dataProperty) or a temp-table + else { + // Creating dummy dataset name: "_localStorage" for tables and arrays + this._dataSetName = "_localStorage"; + tableRef.addRecords(jsonObject, addMode, keyFields); + this._dataSetName = null; + } + }; + + this.getMethodProperties = function(operation, name) { + var idx; + + if (this._resource._operations) { + if (this._resource._operations[operation]) { + return this._resource._operations[operation]; + } + } + else { + this._resource._operations = {}; + } + for (var idx = 0; idx < this._resource.operations.length; idx++) { + if (this._resource.operations[idx].type == operation) { + return (this._resource._operations[operation] = this._resource.operations[idx]); + } + } + }; + + /////////////////////////////////////////////////////////////////////////// + + // Load data + if (autoFill) + this.fill(); + + }; // End of JSDO + + // Constants for progress.data.JSDO + if ((typeof Object.defineProperty) == 'function') { + Object.defineProperty(progress.data.JSDO, 'MODE_APPEND', { + value: 1, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'MODE_EMPTY', { + value: 2, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'MODE_MERGE', { + value: 3, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'MODE_REPLACE', { + value: 4, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'ERROR', { + value: -1, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'APP_ERROR', { + value: -2, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'RETVAL', { + value: -3, + enumerable: true + }); + Object.defineProperty(progress.data.JSDO, 'DATA_ERROR', { + value: -4, + enumerable: true + }); + } else { + progress.data.JSDO.MODE_APPEND = 1; + progress.data.JSDO.MODE_EMPTY = 2; + progress.data.JSDO.MODE_MERGE = 3; + progress.data.JSDO.MODE_REPLACE = 4; + } + + /* CRUD */ + progress.data.JSDO._OP_CREATE = 1; + progress.data.JSDO._OP_READ = 2; + progress.data.JSDO._OP_UPDATE = 3; + progress.data.JSDO._OP_DELETE = 4; + progress.data.JSDO._OP_SUBMIT = 5; + + /* Offline support: saving data to local storage */ + progress.data.JSDO.ALL_DATA = 1; + progress.data.JSDO.CHANGES_ONLY = 2; + + // Arrays elements as individual fields + // Separator must have at least one characters + progress.data.JSDO.ARRAY_INDEX_SEPARATOR = "_"; + +// setup inheritance for JSDO + progress.data.JSDO.prototype = new progress.util.Observable(); + progress.data.JSDO.prototype.constructor = progress.data.JSDO; + progress.data.JSDO.prototype.toString = function (radix) { + return "JSDO"; + }; + +// setup inheritance for table reference + progress.data.JSTableRef.prototype = new progress.util.Observable(); + progress.data.JSTableRef.prototype.constructor = progress.data.JSTableRef; + progress.data.JSTableRef.prototype.toString = function (radix) { + return "JSTableRef"; + }; + + // Built-in Plugins + progress.data.PluginManager.addPlugin("JFP", { + requestMapping: function(jsdo, params, info) { + var sortFields, + field, + fieldName, + fieldInfo, + tableName, + filter, + sortDir, + ablFilter, + sqlQuery, + methodProperties, + capabilities, + index, + position, + option, + capabilitiesObject, + reqCapabilities = { + filter: { options: [ "ablFilter", "sqlQuery" ], mapping: undefined }, + top: { options: [ "top" ], mapping: undefined }, + skip: { options: [ "skip" ], mapping: undefined }, + id: { options: [ "id" ], mapping: undefined }, + sort: { options: [ "orderBy" ], mapping: undefined } + }, + doConversion = true, + param; + + if (info.operation === "read") { + capabilitiesObject = {}; + methodProperties = jsdo.getMethodProperties(info.operation); + capabilities = methodProperties.capabilities; + + if (capabilities) { + capabilities = capabilities.replace(/\s/g, "").split(","); + for (index = 0; index < capabilities.length; index += 1) { + capabilitiesObject[capabilities[index]] = true; + } + } + for (param in params) { + if (param && (params[param] !== undefined) && reqCapabilities[param]) { + for (index = 0; index < reqCapabilities[param].options.length; index += 1) { + option = reqCapabilities[param].options[index]; + if (capabilitiesObject[option]) { + reqCapabilities[param].mapping = option; + break; + } + } + if (!reqCapabilities[param].mapping) { + throw new Error(msg.getMsgText("jsdoMSG120", + reqCapabilities[param].options.join("' or '"), param)); + } + } + } + + if (jsdo._defaultTableRef && params.tableRef === undefined) { + tableName = jsdo._defaultTableRef._name; + } + else { + tableName = params.tableRef; + } + + if (params.sort) { + // Convert sort expression to JFP format + + if (typeof(params.sort) === "object" && !(params.sort instanceof Array)) { + // Kendo UI sort format - object + // Make params.sort an array + params.sort = [params.sort]; + } + sortFields = ""; + for (index = 0; index < params.sort.length; index += 1) { + field = params.sort[index]; + sortDir = ""; + + if (typeof(field) === "string") { + // setSortFields format + // Extract fieldName and sortDir from string + fieldName = field; + position = field.indexOf(":"); + if (position !== -1) { + sortDir = fieldName.substring(position + 1); + fieldName = fieldName.substring(0, position); + switch(sortDir.toLowerCase()) { + case "desc": + case "descending": + sortDir = "desc"; + break; + } + } + } else { + // Kendo UI sort format - array + // Extract fieldName and sortDir from object + fieldName = field.field; + if (params.sort[index].dir === "desc") { + sortDir = params.sort[index].dir; + } + } + if (tableName) { + // Use original fieldName instead of serialized name + fieldInfo = jsdo[tableName]._fields[fieldName.toLowerCase()]; + if (fieldInfo && fieldInfo.origName) { + fieldName = fieldInfo.origName; + } + } + if (sortDir === "desc") { + fieldName += " DESC"; + } + sortFields += fieldName; + if (index < params.sort.length - 1) { + sortFields += ","; + } + } + } + + if (params.filter) { + // If filter is specified as string, then no conversion is necessary + if (typeof params.filter === 'string') { + doConversion = false; + } + + params.tableRef = tableName; + + if (doConversion && (params.tableRef === undefined)) { + throw new Error(msg.getMsgText("jsdoMSG045", "fill() or read()", "params", + "tableRef")); + } + + if (reqCapabilities["filter"].mapping === "ablFilter") { + if (doConversion) { + ablFilter = progress.util._convertToABLWhereString( + jsdo._buffers[params.tableRef], params.filter); + } + else { + ablFilter = params.filter; + } + } + else if (reqCapabilities["filter"].mapping === "sqlQuery") { + if (doConversion) { + sqlQuery = progress.util._convertToSQLQueryString( + jsdo._buffers[params.tableRef], params.filter, true); + } + else { + sqlQuery = params.filter; + } + } + } + + filter = JSON.stringify({ + ablFilter: ablFilter, + sqlQuery: sqlQuery, + orderBy: sortFields, + skip: params.skip, + top: params.top + }); + + params = {filter: filter}; + } + return params; + } + }); + + if (typeof progress.ui == 'undefined') + progress.ui = {}; + progress.ui.UITableRef = function UITableRef(tableRef) { + this._tableRef = tableRef; + this._listview = null; + this._detailPage = null; + this._listviewContent = undefined; + + this.addItem = function (format) { + var detailForm; + + if (!this._tableRef.record) + throw new Error(msg.getMsgText("jsdoMSG002", this._name)); + + if (!this._listview) return; + + format = format ? format : this._listview.format; + detailForm = (this._detailPage && this._detailPage.name) ? this._detailPage.name : ""; + + if (this._listviewContent === undefined) { + this.clearItems(); + } + var text = this._listview.itemTemplate ? + this._listview.itemTemplate : progress.ui.UIHelper._itemTemplate; + + text = text.replace(new RegExp('{__format__}', 'g'), format); + text = text.replace(new RegExp('{__id__}', 'g'), this._tableRef.record.data._id); + text = text.replace(new RegExp('{__page__}', 'g'), detailForm); + + for (var field in this._tableRef.record.data) { + var value = this._tableRef.record.data[field]; + text = text.replace(new RegExp('{' + field + '}', 'g'), + (value !== undefined && value !== null) ? value : ""); + } + + this._listviewContent += text; + }; + + this.clearItems = function () { + if (this._listview) { + this._listviewContent = ''; + var listviewElement = document.getElementById(this._listview.name); + if (listviewElement) { + listviewElement.innerHTML = ''; + } + } + }; + + this._getFormFieldValue = function (fieldName, detailPageName) { + var value = null; + + if (detailPageName === undefined) { + if (this._detailPage && this._detailPage.name) + detailPageName = this._detailPage.name; + } + + if (typeof($) == 'function' && detailPageName) { + field = $("#" + detailPageName + " #" + fieldName); + if (!field || field.length === 0) + field = $("#" + detailPageName + ' [dsid="' + fieldName + '"]'); + if (field && field.length == 1) + value = field.val(); + } + else { + field = document.getElementById(fieldName); + if (field) { + value = field.value; + } + } + + return value; + }; + + this._setFormField = function (fieldName, value, detailPageName) { + var field = null; + + if (detailPageName === undefined) { + if (this._detailPage && this._detailPage.name) + detailPageName = this._detailPage.name; + } + + if (typeof($) == 'function' && detailPageName) { + field = $("#" + detailPageName + " #" + fieldName); + if (!field || field.length === 0) + field = $("#" + detailPageName + ' [dsid="' + fieldName + '"]'); + if (field && field.length == 1) + field.val(value); + } + else { + field = document.getElementById(fieldName); + if (field) { + field.value = value; + } + } + }; + + /* + * Assigns field values from the form. + */ + this.assign = function (detailPageName) { + if (!this._tableRef.record) + throw new Error(msg.getMsgText("jsdoMSG002", this._tableRef._name)); + if ((arguments.length !== 0) && (typeof detailPageName != 'string')) + throw new Error(msg.getMsgText("jsdoMSG024", "UIHelper", "assign()")); + + // Ensure creation of before image record + this._tableRef.record.assign(null); + + var fieldName; + var schema = this._tableRef.getSchema(); + for (var i = 0; i < schema.length; i++) { + fieldName = schema[i].name; + if (fieldName == '_id') continue; + var value = this._getFormFieldValue(fieldName, detailPageName); + // CR OE00241289 Should always copy over field value unless undefined, + // user may have explicitly set it to blank + if (typeof value != 'undefined') { + if (typeof value == 'string' && schema[i].type != 'string') { + value = this._tableRef._jsdo._convertType(value, + schema[i].type, + schema[i].items ? schema[i].items.type : null); + } + this._tableRef.record.data[fieldName] = value; + } + } + + // Ensure order of record + this._tableRef.record._sortRecord(); + + return true; + }; + + this.display = function (pageName) { + if (!this._tableRef.record) + throw new Error(msg.getMsgText("jsdoMSG002", this._tableRef._name)); + + // Display record to form + var schema = this._tableRef.getSchema(); + for (var i = 0; i < schema.length; i++) { + this._setFormField(schema[i].name, this._tableRef.record.data[schema[i].name], pageName); + } + this._setFormField('_id', this._tableRef.record.data._id, pageName); + }; + + this.showListView = function () { + if (!this._listview) return; + + var uiTableRef = this; + var listviewElement; + if (typeof($) == 'function') { + listviewElement = $("#" + this._listview.name); + if (listviewElement && listviewElement.length == 1) { + listviewElement.html(this._listviewContent ? this._listviewContent : ''); + try { + if (listviewElement.attr("data-filter") === "true" + && typeof listviewElement.filterable === "function") { + listviewElement.filterable("refresh"); + } + else { + listviewElement.listview("refresh"); + } + } + catch (e) { + // Workaround for issue with JQuery Mobile throwning exception on refresh + } + } + + if (this._listview.autoLink) { + // Add trigger for 'tap' event to items + $("#" + this._listview.name + " li").each( + function (/* index */) { + $(this).bind('click', + function (/* event, ui */) { + var jsrecord = uiTableRef.getListViewRecord(this); + uiTableRef.display(); + if (typeof(uiTableRef._listview.onSelect) == 'function') { + uiTableRef._listview.onSelect(event, this, jsrecord); + } + }); + }); + } + } + else { + listviewElement = document.getElementById(this._listview.name); + if (listviewElement) { + listviewElement.innerHTML = this._listviewContent; + } + + if (this._listview.autoLink) { + var element = document.getElementById(this._listview.name); + if (element && element.childElementCount > 0) { + for (var i = 0; i < element.children.length; i++) { + element.children[i].onclick = function () { + var jsrecord = uihelper.getListViewRecord(this); + uihelper.display(); + if (typeof(uiTableRef._listview.onSelect) == 'function') { + uiTableRef._listview.onSelect(event, this, jsrecord); + } + }; + } + } + } + } + + this._listviewContent = undefined; + }; + + this.getFormFields = function (fields) { + if (!this._tableRef._schema) + return ''; + if (!(fields instanceof Array)) + fields = null; + else { + var tmpFields = {}; + for (var i = 0; i < fields.length; i++) { + tmpFields[fields[i]] = fields[i]; + } + fields = tmpFields; + } + var htmltext; + if (!fields || fields['_id']) { + htmltext = ''; + } + else + htmltext = ''; + htmltext += '
'; + + for (var i = 0; i < this._tableRef._schema.length; i++) { + var fieldName = this._tableRef._schema[i].name; + if (fieldName == '_id') continue; + if (fieldName.length > 0 && fieldName.charAt(0) == '_') continue; + if (fields && fields[fieldName] === undefined) continue; + var fieldLabel = this._tableRef._schema[i].title ? + this._tableRef._schema[i].title : this._tableRef._schema[i].name; + var text = (this._detailPage && this._detailPage.fieldTemplate) ? + this._detailPage.fieldTemplate : progress.ui.UIHelper._fieldTemplate; + text = text.replace(new RegExp('{__label__}', 'g'), fieldLabel); + text = text.replace(new RegExp('{__name__}', 'g'), this._tableRef._schema[i].name); + htmltext += text; + } + htmltext += '
'; + fields = null; + return htmltext; + }; + + this.getListViewRecord = function (htmlIElement) { + var id = htmlIElement.getAttribute('data-id'); + return this._tableRef.findById(id); + }; + + this.getFormRecord = function (detailPageName) { + var id = this._getFormFieldValue('_id', detailPageName); + return this._tableRef.findById(id); + }; + + this._getIdOfElement = function (name) { + if (typeof($) == 'function') { + var element = $("#" + name); + if (!element || element.length === 0) { + element = $('[dsid="' + name + '"]'); + if (element && element.length == 1) { + var id = element.attr("id"); + if (id) + return id; + } + } + } + return name; + }; + + this.setDetailPage = function setDetailPage(obj) { + if (!obj || (typeof(obj) != 'object')) + throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "object")); + if (!obj.name || (typeof(obj.name) != 'string')) + throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "name")); + this._detailPage = obj; + this._detailPage.name = this._getIdOfElement(this._detailPage.name); + }; + this.setListView = function setListView(obj) { + if (!obj || (typeof(obj) != 'object')) + throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "object")); + if (!obj.name || (typeof(obj.name) != 'string')) + throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "name")); + if (obj.format && (typeof(obj.name) != 'string')) + throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "format")); + + this._listview = obj; + this._listview.name = this._getIdOfElement(this._listview.name); + if (!this._listview.format) { + if (typeof($) == 'function') { + for (var i = 0; i < this._tableRef._schema.length; i++) { + var fieldName = this._tableRef._schema[i].name; + + field = $("#" + this._listview.name + ' [dsid="' + fieldName + '"]'); + if (field && field.length == 1) { + field.html('{' + fieldName + '}'); + } + } + } + var text = document.getElementById(this._listview.name).innerHTML; + var pos = text.indexOf(''; + progress.ui.UIHelper._defaultFieldTemplate = '
' + + '' + + '
'; + progress.ui.UIHelper._itemTemplate = progress.ui.UIHelper._defaultItemTemplate; + progress.ui.UIHelper._fieldTemplate = progress.ui.UIHelper._defaultFieldTemplate; + + progress.ui.UIHelper.setItemTemplate = function (template) { + progress.ui.UIHelper._itemTemplate = template ? template : progress.ui.UIHelper._defaultItemTemplate; + }; + + progress.ui.UIHelper.setFieldTemplate = function (template) { + progress.ui.UIHelper._fieldTemplate = + template ? template : progress.ui.UIHelper._defaultFieldTemplate; + }; + +})(); + +//this is so that we can see the code in Chrome's Source tab when script is loaded via XHR +//# sourceURL=progress.jsdo.js +/* +progress.session.js Version: 4.4.1-2 + +Copyright (c) 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + /* define these if not defined yet - they may already be defined if + progress.js was included first */ + if (typeof progress === "undefined") { + progress = {}; + } + if (typeof progress.data === "undefined") { + progress.data = {}; + } + + progress.data.ServicesManager = {}; + progress.data.ServicesManager._services = []; + progress.data.ServicesManager._resources = []; + progress.data.ServicesManager._data = []; + progress.data.ServicesManager._sessions = []; + progress.data.ServicesManager._jsdosessions = []; + /* + progress.data.ServicesManager.put = function(id, jsdo) { + progress.data.ServicesManager._data[id] = jsdo; + }; + progress.data.ServicesManager.get = function(id) { + return progress.data.ServicesManager._data[id]; + }; + */ + + progress.data.ServicesManager.addResource = function (id, resource) { + if (progress.data.ServicesManager._resources[id] === undefined) + progress.data.ServicesManager._resources[id] = resource; + else + throw new Error("A resource named '" + id + "' was already loaded."); + }; + progress.data.ServicesManager.getResource = function (id) { + return progress.data.ServicesManager._resources[id]; + }; + progress.data.ServicesManager.addService = function (id, service) { + if (progress.data.ServicesManager._services[id] === undefined) + progress.data.ServicesManager._services[id] = service; + else + throw new Error("A service named '" + id + "' was already loaded."); + }; + progress.data.ServicesManager.getService = function (id) { + return progress.data.ServicesManager._services[id]; + }; + progress.data.ServicesManager.addSession = function (catalogURI, session) { + if (progress.data.ServicesManager._sessions[catalogURI] === undefined) + progress.data.ServicesManager._sessions[catalogURI] = session; + else + throw new Error("Cannot load catalog '" + catalogURI + "' multiple times."); + }; + + progress.data.ServicesManager.addJSDOSession = function (catalogURI, jsdosession) { + if (progress.data.ServicesManager._jsdosessions[catalogURI] === undefined) { + progress.data.ServicesManager._jsdosessions[catalogURI] = jsdosession; + } + else { + throw new Error("Cannot load catalog '" + catalogURI + "' multiple times."); + } + }; + progress.data.ServicesManager.getSession = function (catalogURI) { + try { + return progress.data.ServicesManager._sessions[catalogURI]; + } + catch (e) { + return null; + } + }; + + progress.data.ServicesManager.cleanSession = function (session) { + var servicesKey, + resourcesKey, + sessionsKey, + service, + services = progress.data.ServicesManager._services, + resources = progress.data.ServicesManager._resources, + sessions = progress.data.ServicesManager._sessions, + jsdosessions = progress.data.ServicesManager._jsdosessions; + + // Delete the services and resources in the ServicesManager + // associated with the Session given + for (servicesKey in services) { + service = null; + if (services[servicesKey]._session === session) { + service = services[servicesKey]; + delete services[servicesKey]; + } + + if (!service) { + continue; + } + + for (resourcesKey in resources) { + if (resources[resourcesKey].service === service) { + delete resources[resourcesKey]; + } + } + } + + // Delete the session and jsdosession from the ServicesManager + for (sessionsKey in sessions) { + if (sessions[sessionsKey] === session) { + delete sessions[sessionsKey]; + + if(jsdosessions[sessionsKey]) { + delete jsdosessions[sessionsKey]; + } + } + } + }; + + /* + * Scans URL for parameters of the form {name} + * Returns array with the names + */ + function extractParamsFromURL(url) { + var urlParams = []; + if (typeof(url) == 'string') { + var paramName = null; + for (var i = 0; i < url.length; i++) { + if (url.charAt(i) == '{') { + paramName = ""; + } + else if (url.charAt(i) == '}') { + if (paramName) + urlParams.push(paramName); + paramName = null; + } + else if (paramName !== null) { + paramName += url.charAt(i); + } + } + } + return urlParams; + } + + /* + * Adds the catalog.json file provided by the catalog parameter, which is a JSDO + * that has loaded the catalog + */ + progress.data.ServicesManager.addCatalog = function (services, session) { + if (!services) { + throw new Error("Cannot find 'services' property in catalog file."); + } + if (services instanceof Array) { + + // first check if there are duplicates before we add them to our cache, + // which only handles unique values + for (var j = 0; j < services.length; j++) { + // don't allow services with the same name across sessions + if (progress.data.ServicesManager.getService(services[j].name) !== undefined) + throw new Error("A service named '" + services[j].name + "' was already loaded."); + + var resources = services[j].resources; + + if (resources instanceof Array) { + for (var i = 0; i < resources.length; i++) { + if (progress.data.ServicesManager.getResource(resources[i].name) !== undefined) + throw new Error("A resource named '" + resources[i].name + + "' was already loaded."); + } + } + else { + throw new Error("Missing 'resources' array in catalog."); + } + } + + for (var j = 0; j < services.length; j++) { + services[j]._session = session; + this.addService(services[j].name, services[j]); // Register the service + var resources = services[j].resources; + var baseAddress = services[j].address; + if (resources instanceof Array) { + for (var i = 0; i < resources.length; i++) { + var resource = resources[i]; + resource.fn = {}; + resource.service = services[j]; + resources[i].url = baseAddress + resources[i].path; + // Register resource + progress.data.ServicesManager.addResource(resources[i].name, resources[i]); + + // Process schema + resource.fields = null; + resource.primaryKeys = null; + if (resource.schema) { + resource.fields = {}; + resource.primaryKeys = {}; + resource._dataSetName = undefined; + resource._tempTableName = undefined; + var properties = null; + + try { + if (typeof resource.schema.properties != 'undefined') { + var keys = Object.keys(resource.schema.properties); + properties = resource.schema.properties; + if (keys.length == 1) { + if (typeof resource.schema.properties[keys[0]].properties != + 'undefined') { + // Schema corresponds to a DataSet + resource._dataSetName = keys[0]; + } + else if (typeof resource.schema.properties[keys[0]].items != + 'undefined') { + // Schema corresponds to a temp-table + resource.dataProperty = keys[0]; + properties = resource.schema.properties[keys[0]].items.properties; + resource._tempTableName = resource.dataProperty; + resource.primaryKeys[resource._tempTableName] = + resource.schema.properties[keys[0]].primaryKey; + } + } + } + else { + var keys = Object.keys(resource.schema); + if (keys.length == 1) { + resource.dataProperty = keys[0]; + if (typeof resource.schema[keys[0]].items != 'undefined') { + // Catalog format correspond to Table Schema + properties = resource.schema[keys[0]].items.properties; + resource._tempTableName = resource.dataProperty; + resource.primaryKeys[resource._tempTableName] = + resource.schema[keys[0]].primaryKey; + } + else if (typeof resource.schema[keys[0]].properties != 'undefined') { + // Catalog format correspond to DataSet Schema + resource._dataSetName = keys[0]; + resource.dataProperty = null; + properties = resource.schema; + } + } + } + } + catch (e) { + throw new Error("Error parsing catalog file."); + } + if (properties) { + if (resource._dataSetName) { + properties = properties[resource._dataSetName].properties; + for (var tableName in properties) { + resource.fields[tableName] = []; + resource.primaryKeys[tableName] = properties[tableName].primaryKey; + var tableProperties; + if (properties[tableName].items + && properties[tableName].items.properties) { + tableProperties = properties[tableName].items.properties; + } + else { + tableProperties = properties[tableName].properties; + } + for (var field in tableProperties) { + tableProperties[field].name = field; + if (field != '_id') + resource.fields[tableName].push(tableProperties[field]); + } + } + } + else { + var tableName = resource.dataProperty ? resource.dataProperty : ""; + resource.fields[tableName] = []; + for (var field in properties) { + properties[field].name = field; + if (field != '_id') + resource.fields[tableName].push(properties[field]); + } + } + } + else + throw new Error("Error parsing catalog file."); + } + else + resource.fields = null; + + // Validate relationship property + if ((resource.relations instanceof Array) + && resource.relations[0] + && resource.relations[0].RelationName) { + throw new Error( + "Relationship properties in catalog must begin with lowercase."); + } + // Process operations + resource.generic = {}; + if (resource.operations) { + for (var idx = 0; idx < resource.operations.length; idx++) { + if (resource.operations[idx].path) { + resource.operations[idx].url = + resource.url + resource.operations[idx].path; + } + else { + resource.operations[idx].url = resource.url; + } + if (!resource.operations[idx].params) { + resource.operations[idx].params = []; + } + if (!resource.operations[idx].type) { + resource.operations[idx].type = "INVOKE"; + } + + // Set opname - validation of opname is done later + var opname = resource.operations[idx].type.toLowerCase(); + + // Set default verb based on operation + if (!resource.operations[idx].verb) { + switch (opname) { + case 'create': + resource.operations[idx].verb = "POST"; + break; + case 'read': + resource.operations[idx].verb = "GET"; + break; + case 'update': + case 'invoke': + case 'submit': + case 'count': + resource.operations[idx].verb = "PUT"; + break; + case 'delete': + resource.operations[idx].verb = "DELETE"; + break; + default: + break; + } + } + + // Point fn to operations + var func = function fn(object, async) { + var deferred; + + // Add static variable fnName to function + if (typeof fn.fnName == 'undefined') { + fn.fnName = arguments[0]; // Name of function + fn.definition = arguments[1]; // Operation definition + return; + } + + var reqBody = null; + var url = fn.definition.url; + var jsdo = this; + var xhr = null; + + var request = {}; + if (object) { + if (typeof(object) != "object") { + throw new Error("Catalog error: Function '" + + fn.fnName + "' requires an object as a parameter."); + } + var objParam; + if (object instanceof XMLHttpRequest + || (object.constructor + && object.constructor.name === "XMLHttpRequest")) { + jsdo = object.jsdo; + xhr = object; + objParam = xhr.objParam; + + // use the request from the xhr request if possible + request = xhr.request; + } + else { + objParam = object; + } + + if (typeof async == 'undefined') { + async = this._async; + } + else { + async = Boolean(async); + } + + request.objParam = objParam; + + + // Process objParam + var isInvoke = (fn.definition.type.toUpperCase() == 'INVOKE'); + for (var i = 0; i < fn.definition.params.length; i++) { + var name = fn.definition.params[i].name; + switch (fn.definition.params[i].type) { + case 'PATH': + case 'QUERY': + case 'MATRIX': + var value = null; + if (objParam) + value = objParam[name]; + if (!value) + value = ""; + if (url.indexOf('{' + name + '}') == -1) { + throw new Error("Catalog error: Reference to " + + fn.definition.params[i].type + " parameter '" + + name + "' is missing in path."); + } + url = url.replace( + new RegExp('{' + name + '}', 'g'), + encodeURIComponent(value)); + break; + case 'REQUEST_BODY': + case 'REQUEST_BODY,RESPONSE_BODY': + case 'RESPONSE_BODY,REQUEST_BODY': + if (xhr && !reqBody) { + reqBody = objParam; + } + else { + var reqParam = objParam[name]; + if (isInvoke + && (fn.definition.params[i].xType + && ("DATASET,TABLE".indexOf( + fn.definition.params[i].xType) != -1))) { + var unwrapped = (jsdo._resource.service.settings + && jsdo._resource.service.settings.unwrapped); + if (unwrapped) { + // Remove extra level if found + if ((typeof(reqParam) == 'object') + && (Object.keys(reqParam).length == 1) + && (typeof(reqParam[name]) == 'object')) + reqParam = reqParam[name]; + } + else { + // Add extra level if not found + if ((typeof(reqParam) == 'object') + && (typeof(reqParam[name])=='undefined')){ + reqParam = {}; + reqParam[name] = objParam[name]; + } + } + } + if (!reqBody) { + reqBody = {}; + } + reqBody[name] = reqParam; + } + break; + case 'RESPONSE_BODY': + break; + default: + throw new Error("Catalog error: " + + "Unexpected parameter type '" + + fn.definition.params[i].type + "'."); + } + } + + // URL has parameters + if (url.indexOf('{') != -1) { + var paramsFromURL = extractParamsFromURL(url); + for (var i = 0; i < paramsFromURL.length; i++) { + var name = paramsFromURL[i]; + var value = null; + if (objParam) + value = objParam[name]; + if (!value) + value = ""; + if (typeof(value) === "object") { + value = JSON.stringify(value); + } + url = url.replace( + new RegExp('{' + name + '}', 'g'), + encodeURIComponent(value)); + } + } + } + + request.fnName = fn.fnName; + request.async = async; + + if (request.deferred === undefined && + typeof($) == 'function' && typeof($.Deferred) == 'function') { + deferred = $.Deferred(); + request.deferred = deferred; + } + + var data = jsdo._httpRequest(xhr, fn.definition.verb, + url, reqBody, request, async); + return data; + }; + // End of Function Definition + + switch (resource.operations[idx].verb.toLowerCase()) { + case 'get': + case 'post': + case 'put': + case 'delete': + break; + default: + throw new Error("Catalog error: Unexpected HTTP verb '" + + resource.operations[idx].verb + + "' found while parsing the catalog."); + } + + switch (opname) { + case 'invoke': + break; + case 'create': + case 'read': + case 'update': + case 'delete': + case 'submit': + case 'count': + if (typeof(resource.generic[opname]) == "function") { + throw new Error("Catalog error: Multiple '" + + resource.operations[idx].type + + "' operations specified in the catalog for resource '" + + resource.name + "'."); + } + else + resource.generic[opname] = func; + break; + default: + throw new Error("Catalog error: Unexpected operation '" + + resource.operations[idx].type + + "' found while parsing the catalog."); + } + + // Set fnName + var name = resource.operations[idx].name; + if (opname === "invoke" || opname === "count") { + resource.fn[name] = {}; + resource.fn[name]["function"] = func; + } + else { + name = "_" + opname; + } + func(name, resource.operations[idx]); + } + } + } + } + } + } + else { + throw new Error("Missing 'services' array in catalog."); + } + + }; + + /* + * Prints debug information about the ServicesManager. + */ + progress.data.ServicesManager.printDebugInfo = function (resourceName) { + if (resourceName) { + //console.log("** ServicesManager **"); + //console.log("** BEGIN **"); + var resource = progress.data.ServicesManager.getResource(resourceName); + if (resource) { + var cSchema = "Schema:\n"; + var cOperations = "Operations: " + resource.operations.length + "\n"; + for (var field in resource.schema.properties) { + cSchema += "\nName: " + field + + "\n"; + } + + for (var i = 0; i < resource.operations.length; i++) { + cOperations += "\n" + i + + "\nName: " + resource.operations[i].name + + "\nURL: " + resource.operations[i].url + + "\ntype: " + resource.operations[i].type + + "\nverb: " + resource.operations[i].verb + + "\nparams: " + resource.operations[i].params.length + + "\n"; + } + console.log("** DEBUG INFO **\nResource name: %s\nURL:%s\n%s\n%s\n\n", + resource.name, resource.url, cSchema, cOperations); + } + else + console.log("Resource not found"); + //console.log("** END **"); + } + }; + + + /* + * Contains information about a server-side Mobile service. + * Properties of args parameter for constructor: + * @param name the name of the service + * @param uri the URI of the service + */ + progress.data.MobileServiceObject = function MobileServiceObject(args) { + var _name = args.name; + Object.defineProperty(this, 'name', + { + get: function () { + return _name; + }, + enumerable: true + }); + + var _uri = args.uri; + Object.defineProperty(this, 'uri', + { + get: function () { + return _uri; + }, + enumerable: true + }); + }; + + /* + An object that maintains the X-CLIENT-PROPS header string + The data for the string is stored in the internal variable named contextObject and is + always up to date. The internal var contextString isn't created until the first time it's + needed (the first get of the contextHeader property), and then it's updated an cached + A call to setContext or setContextProperty updates contextObject but sets contextString to + null, which signals that it needs to be updated. If contextObject is an empty object, + contextString is set to undefined to indicate that no header is to be sent + */ + progress.data.ContextProperties = function() { + var contextObject = {}, + contextString; // if null, contextObject has been changed but string wasn't updated yet + + // the string to be sent in the X-CLIENT-PROPS header (unless Session.xClientProps has been set) + Object.defineProperty(this, 'contextHeader', + { + get: function () { + var header; + + if (contextString === null) { // needs to be updated + header = JSON.stringify( contextObject ); + if (header === "{}") { + contextString = undefined; + } + else { + contextString = header; + } + } + // else (contextString === undefined || has a usable value) + + return contextString; + }, + enumerable: true + }); + + /* determine whether the property is already present, and - + add it if it's not present + remove it if propertyValue is explicitly passed as undefined + otherwise replace its value (even if the new value is null or "") + */ + this.setContextProperty = function( propertyName, propertyValue) { + if (arguments.length < 2) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', + 'setContextProperty', 2)); + } + if (arguments.length !== 2) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", "Session", + "setContextProperty", 2)); + } + if (typeof propertyName !== "string") { + // {1}: Parameter {1} must be of type {3} in {4} call. + throw new Error(progress.data._getMsgText("jsdoMSG121", 'Session', 1, 'string', + 'setContextProperty')); + } + + if ( propertyValue === undefined ) { + delete contextObject[propertyName]; // OK if it doesn't exist -- no error + } + else { + contextObject[propertyName] = propertyValue; + } + contextString = null; // must be updated on next get of this.contextHeader + }; + + this.setContext = function( context ) { + var prop; + + if (arguments.length < 1) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'setContext', 1)); + } + if (arguments.length > 1) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'setContext', 1)); + } + if ( typeof context == "object" ) { + /* Copy the properties of the context passed in as an argument into + * an internal contextObject. (Note that if the context object passed in + * has a prototype, this code copies them, too) + */ + contextObject = {}; + for (prop in context) { + if( context.hasOwnProperty(prop) ) { + if (typeof context[prop] !== "function" ) { + contextObject[prop] = context[prop]; + } + } + } + } + else if ( (context === undefined) || (context === null) ) { + contextObject = {}; + } + else { + // {1}: Parameter {1} must be of type {3} in {4} call. + throw new Error(progress.data._getMsgText("jsdoMSG121", 'Session', 1, 'Object', + 'setContextProperty')); + } + contextString = null; // must be updated on next get of this.contextHeader + }; + + this.getContext = function( ) { + if (arguments.length > 0) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContext', 0)); + } + return contextObject; + }; + + this.getContextProperty = function( propertyName) { + if (arguments.length < 1) { + // {1}: Incorrect number of arguments in {2} call. There should be {3}. + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContextProperty', 1)); + } + if (arguments.length > 1) { + // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; + throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContextProperty', 1)); + } + return contextObject[propertyName]; + }; + + }; // end of ContextProperties + + /* + * Manages authentication and session ID information for a service. + * + * Use: OE mobile developer instantiates a session and calls addCatalog() to load + * information for one or more services defined in a catalog file. + * + * Developer instantiates JDSOs as needed. + * Usually all of the JSDOs will use the same session, but if a client-side + * service needs resources from more than one REST app, there would need to be more + * than one session + * + */ + progress.data.Session = function Session(options) { + + var defPropSupported = false; + if ((typeof Object.defineProperty) == 'function') { + defPropSupported = true; + } + + var that = this, + jsdosession, // "backpointer" if this Session is being used by a JSDOSession + isUserAgentiOS = false, // checked just below this var statement + isFirefox = false, // checked just below this var statement + isEdge = false, // checked just below this var statement + isIE = false, // checked just below this var statement + canPassCredentialsToOpenWithCORS = false, // False will always work if creds are correct + defaultiOSBasicAuthTimeout = 4000, + deviceIsOnline = true, // online until proven offline + restApplicationIsOnline = false, // was the Mobile Web Application that this Session object + // connects to online the last time it was checked? + // (value is always false if session is not logged in) + oepingAvailable = false, + defaultPartialPingURI = "/rest/_oeping", + partialPingURI = defaultPartialPingURI, + _storageKey, + _authProvider = null, + customCredentials = false, + + // Note: the variables above here are used during the lifetime of the object; the ones below + // are only used while the constructor is executing + storedAuthModel, + storedURI, + newURI, + stateWasReadFromStorage = false; + + // This is a hidden argument to suppress this warning and be re-used for future warnings + if (!options || options._silent !== true) { + console.warn("Session: As of JSDO 4.4, the Session object has been deprecated. Please use the JSDOSession object instead."); + } + + if (typeof navigator !== "undefined") { + if (typeof navigator.userAgent !== "undefined") { + isUserAgentiOS = navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)/i); + isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + // detect that we're running in MS Edge browser + isEdge = navigator.userAgent.indexOf('Edge/') > -1; + // detect that we're running in IE 11 (or IE 11 in pre-11 mode) or IE 10 browser + isIE = ( (navigator.userAgent.indexOf('Trident/')) > -1 || (navigator.userAgent.indexOf('MSIE 10') > -1)); + } + } + + // Firefox, Edge, and IE will throw an error on the send() if CORS is being used for the request + // and we have included credentials in the URI (which is what passing them to open() does), + canPassCredentialsToOpenWithCORS = !(isFirefox || isEdge || isIE); + + // When using basic authentication, we can pass the user name and password to the XMLHttpRequest.open() + // method. However, in some browsers, passing credentials to open() will result in the xhr's .send() + // method throwing an error. The goal of this function is to figure out whether it's safe to include + // the credentials. It returns false if there could be a problem, true otherwise. + // Note: currently it does this solely on the basis of what browser we are running in, regardless + // of whether the request will actually use the CORS protocol. Ideally, we should take into account whether + // the request will actually require CORS. The question is whether we can reliably do that. + // The reason for taking the specific request into account is that there are drawbacks to not passing the + // credentials when we are NOT using CORS, namely that if the credentials are invalid, some browsers will + // put up their own prompt for credentials in non-CORS situations (those browsers are IE, Edge, and Chrome) + function canPassCredentialsToOpen() { + return canPassCredentialsToOpenWithCORS; + } + + this._onlineHandler = function () { + setDeviceIsOnline(true); + that.trigger("online", that, null); + }; + + this._offlineHandler = function () { + setDeviceIsOnline(false); + that.trigger("offline", that, progress.data.Session.DEVICE_OFFLINE, null); + }; + + if ((typeof window != 'undefined' ) && (window.addEventListener)) { + window.addEventListener("online", this._onlineHandler, false); + window.addEventListener("offline", this._offlineHandler, false); + } + + /* constants and properties - define them as properties via the defineProperty() + * function, which has "writable" and "configurable" parameters that both + * default to false, so these calls create properties that are read-only + * + * IF WE DECIDE THAT WE CAN ASSUME WE ALWAYS RUN WITH A VERSION OF JAVASCRIPT THAT SUPPORTS + * Object.DefineProperty(), WE CAN DELETE THE defPropSupported VARIABLE, THE TEST OF IT BELOW, + * AND THE 'ELSE' CLAUSE BELOW AND ALL THE setXxxx functions (AND CHANGE THE CALLS TO THE setXxxx + * FUNCTIONS SO THEY JUST REFER TO THE PROPERTY) + * + */ + + // define these unconditionally so we don't get a warning on the push calls that they might + // have been uninitialized + var _catalogURIs = []; + var _services = []; + var _jsdos = []; + + this.onOpenRequest = null; + + var _password = null; + + if (defPropSupported) { + var _userName = null; + Object.defineProperty(this, 'userName', + { + get: function () { + return _userName; + }, + enumerable: true + }); + + var _loginTarget = '/static/home.html'; + Object.defineProperty(this, 'loginTarget', + { + get: function () { + return _loginTarget; + }, + enumerable: true + }); + + var _serviceURI = null; + Object.defineProperty(this, 'serviceURI', + { + get: function () { + return _serviceURI; + }, + enumerable: true + }); + + Object.defineProperty(this, 'catalogURIs', + { + get: function () { + return _catalogURIs; + }, + enumerable: true + }); + + Object.defineProperty(this, 'services', + { + get: function () { + return _services; + }, + enumerable: true + }); + + var _loginResult = null; + Object.defineProperty(this, 'loginResult', + { + get: function () { + return _loginResult; + }, + enumerable: true + }); + + var _loginHttpStatus = null; + Object.defineProperty(this, 'loginHttpStatus', + { + get: function () { + return _loginHttpStatus; + }, + enumerable: true + }); + + var _clientContextId = null; + Object.defineProperty(this, 'clientContextId', + { + get: function () { + return _clientContextId; + }, + enumerable: true + }); + + var _authenticationModel = progress.data.Session.AUTH_TYPE_ANON; + Object.defineProperty(this, 'authenticationModel', + { + get: function () { + return _authenticationModel; + }, + set: function (newval) { + if (newval) { + newval = newval.toLowerCase(); + } + switch (newval) { + case progress.data.Session.AUTH_TYPE_FORM : + case progress.data.Session.AUTH_TYPE_BASIC : + case progress.data.Session.AUTH_TYPE_ANON : + case progress.data.Session.AUTH_TYPE_SSO : + case null : + _authenticationModel = newval; + storeSessionInfo("authenticationModel", newval); + break; + default: + throw new Error("Error setting Session.authenticationModel. '" + + newval + "' is an invalid value."); + } + }, + enumerable: true + }); + + var _lastSessionXHR = null; + Object.defineProperty(this, 'lastSessionXHR', + { + get: function () { + return _lastSessionXHR; + }, + enumerable: true + }); + + Object.defineProperty(this, 'connected', + { + get: function () { + return (this.loginResult === progress.data.Session.LOGIN_SUCCESS) + && restApplicationIsOnline + && deviceIsOnline; + }, + enumerable: true + }); + + Object.defineProperty(this, 'JSDOs', + { + get: function () { + return _jsdos; + }, + enumerable: true + }); + + var _pingInterval = 0; + var _timeoutID = null; + Object.defineProperty(this, 'pingInterval', + { + get: function () { + return _pingInterval; + }, + set: function (newval) { + if ( (typeof newval === "number") && (newval >= 0) ) { + _pingInterval = newval; + storeSessionInfo("pingInterval", newval); + if (newval > 0) { + // if we're logged in, start autopinging + if (this.loginResult === progress.data.Session.LOGIN_SUCCESS) { + _timeoutID = setTimeout(this._autoping, newval); + } + } + else if (newval === 0) { + clearTimeout(_timeoutID); + _pingInterval = 0; + } + } + else { + throw new Error("Error setting Session.pingInterval. '" + + newval + "' is an invalid value."); + } + }, + enumerable: true + }); + + var _contextProperties = new progress.data.ContextProperties(); + Object.defineProperty( this, + "_contextProperties", + { + get: function () { + return _contextProperties; + }, + enumerable: false + } + ); + + var isInvalidated = false; + Object.defineProperty( + this, + "_isInvalidated", + { + get: function () { + return isInvalidated; + }, + enumerable: false + } + ); + + // used internally, not supported as part of the Session API (tho authProvider is part + // of the *JSDOSession* API) + Object.defineProperty( this, + "_authProvider", + { + get: function () { + return _authProvider; + }, + set: function(newval) { + if (_authProvider) { + throw new Error("Internal Error setting Session._authProvider. '" + + "The property has already been set."); + + } else { + setAuthProvider(newval); + } + }, + enumerable: false + } + ); + } + else { + this.userName = null; + this.loginTarget = '/static/home.html'; + this.serviceURI = null; + this.catalogURIs = []; + this.services = []; + this.loginResult = null; + this.loginHttpStatus = null; + this.clientContextId = null; + this.authenticationModel = progress.data.Session.AUTH_TYPE_ANON; + this.lastSessionXHR = null; + } + + // stores data value using the JSDOSession's storage key plus the infoName + // argument as a key. If there is no infoName, just uses the storage key + // by itself (the latter case is intended to serve as a flag that we have + // stored this JSDOSession's data before) + // + function storeSessionInfo(infoName, value) { + var key; + if (that.loginResult === progress.data.Session.LOGIN_SUCCESS && + typeof (sessionStorage) === 'object' && _storageKey) { + + key = _storageKey; + if (infoName) { + key = key + "." + infoName; + } + if (typeof (value) !== 'undefined') { + sessionStorage.setItem(key, JSON.stringify(value)); + } + } + } + + function retrieveSessionInfo(infoName) { + var key, + jsonStr, + value = null; + if (typeof (sessionStorage) === 'object' && _storageKey) { + key = _storageKey; + if (infoName) { + key = key + "." + infoName; + } + jsonStr = sessionStorage.getItem(key); + if (jsonStr !== null) { + try { + value = JSON.parse(jsonStr); + } catch (e) { + value = null; + } + } + return value; + } + } + + function clearSessionInfo(infoName) { + var key; + if (typeof (sessionStorage) === 'object' && _storageKey) { + key = _storageKey; + if (infoName) { + key = key + "." + infoName; + sessionStorage.removeItem(key); + } + } + } + + function storeAllSessionInfo() { + if (_storageKey) { + storeSessionInfo("loginResult", that.loginResult); + storeSessionInfo("userName", that.userName); + storeSessionInfo("serviceURI", that.serviceURI); + storeSessionInfo("loginHttpStatus", that.loginHttpStatus); + storeSessionInfo("authenticationModel", that.authenticationModel); + storeSessionInfo("pingInterval", that.pingInterval); + storeSessionInfo("oepingAvailable", oepingAvailable); + storeSessionInfo("partialPingURI", partialPingURI); + storeSessionInfo("clientContextId", that.clientContextId); + storeSessionInfo("deviceIsOnline", deviceIsOnline); + storeSessionInfo("restApplicationIsOnline", restApplicationIsOnline); + if (that._authProvider) { + storeSessionInfo("_authProvider.init", + {uri: that._authProvider.uri, + authenticationModel: that._authProvider.authenticationModel}); + } + storeSessionInfo(_storageKey, true); + } + } + + function clearAllSessionInfo() { + if (_storageKey) { + if (retrieveSessionInfo(_storageKey)) { + clearSessionInfo("loginResult"); + clearSessionInfo("userName"); + clearSessionInfo("serviceURI"); + clearSessionInfo("loginHttpStatus"); + clearSessionInfo("clientContextId"); + clearSessionInfo("deviceIsOnline"); + clearSessionInfo("restApplicationIsOnline"); + clearSessionInfo("authenticationModel"); + clearSessionInfo("pingInterval"); + clearSessionInfo("oepingAvailable"); + clearSessionInfo("partialPingURI"); + clearSessionInfo("_authProvider.init"); + clearSessionInfo(_storageKey); + } + } + } + + function setSessionInfoFromStorage(key) { + var authproviderInitObject, + authProvider; + if (retrieveSessionInfo(key)) { + setLoginResult(retrieveSessionInfo("loginResult"), this); + setUserName(retrieveSessionInfo("userName"), this); + setServiceURI(retrieveSessionInfo("serviceURI"), this); + setLoginHttpStatus(retrieveSessionInfo("loginHttpStatus"), this); + setClientContextID(retrieveSessionInfo("clientContextId"), this); + setDeviceIsOnline(retrieveSessionInfo("deviceIsOnline")); + setRestApplicationIsOnline(retrieveSessionInfo("restApplicationIsOnline")); + that.authenticationModel = retrieveSessionInfo("authenticationModel"); + that.pingInterval = retrieveSessionInfo("pingInterval"); + setOepingAvailable(retrieveSessionInfo("oepingAvailable")); + setPartialPingURI(retrieveSessionInfo("partialPingURI")); + // if information on an AuthenticationProvider for the session is in storage, and if + // the authProvider hasn't already been set for this Session, create a new authProvider + // using the same info as the old one. This would be likely to happen if the app's code + // had used the old JSDOSession.login API, where we create the AuthenticationProvider + // automatically during login instead of the code passing one to the constructor + if (!that._authProvider) { + authproviderInitObject = retrieveSessionInfo("_authProvider.init"); + if (authproviderInitObject) { + setAuthProvider(new progress.data.AuthenticationProvider(authproviderInitObject)); + } + } + } + } + + function setUserName(newname, sessionObject) { + if (defPropSupported) { + _userName = newname; + } + else { + sessionObject.userName = newname; + } + + storeSessionInfo("userName", newname); + } + + function setLoginTarget(target, sessionObject) { + if (defPropSupported) { + _loginTarget = target; + } + else { + sessionObject.loginTarget = target; + } + } + + function setServiceURI(url, sessionObject) { + if (defPropSupported) { + _serviceURI = url; + } + else { + sessionObject.serviceURI = url; + } + + storeSessionInfo("serviceURI", url); + } + + function pushCatalogURIs(url, sessionObject) { + if (defPropSupported) { + _catalogURIs.push(url); + } + else { + sessionObject.catalogURIs.push(url); + } + } + + function pushService(serviceObject, sessionObject) { + if (defPropSupported) { + _services.push(serviceObject); + } + else { + sessionObject.services.push(serviceObject); + } + } + + function findService(serviceName) { + for (var prop in _services) { + var srv = _services[prop]; + if (srv.name === serviceName) { + return srv; + } + } + return null; + } + + function setLoginResult(result, sessionObject) { + if (defPropSupported) { + _loginResult = result; + } else { + sessionObject.loginResult = result; + } + + if (result === progress.data.Session.LOGIN_SUCCESS) { + storeSessionInfo("loginResult", result); + } else { + // Let's clear sessionStorage since we logged out or something went bad! + clearAllSessionInfo(); + } + } + + function setLoginHttpStatus(status, sessionObject) { + if (defPropSupported) { + _loginHttpStatus = status; + } + else { + sessionObject.loginHttpStatus = status; + } + + storeSessionInfo("loginHttpStatus", status); + } + + function setClientContextIDfromXHR(xhr, sessionObject) { + if (xhr) { + setClientContextID(getResponseHeaderNoError(xhr, "X-CLIENT-CONTEXT-ID"), sessionObject); + } + } + + function setClientContextID(ccid, sessionObject) { + if (defPropSupported) { + _clientContextId = ccid; + } + else { + sessionObject.clientContextId = ccid; + } + + storeSessionInfo("clientContextId", ccid); + } + + function setLastSessionXHR(xhr, sessionObject) { + if (defPropSupported) { + _lastSessionXHR = xhr; + } + else { + sessionObject.lastSessionXHR = xhr; + } + } + + function setDeviceIsOnline(value) { + deviceIsOnline = value; + + storeSessionInfo("deviceIsOnline", value); + } + + function setAuthProvider(value) { + // Do this to preserve authprovider's null-ness. + _authProvider = value ? value : null; + } + + function setRestApplicationIsOnline(value) { + restApplicationIsOnline = value; + + storeSessionInfo("restApplicationIsOnline", value); + } + + function setOepingAvailable(value) { + oepingAvailable = value; + + storeSessionInfo("oepingAvailable", value); + } + + function setPartialPingURI(value) { + partialPingURI = value; + + storeSessionInfo("partialPingURI", value); + } + + /* + When using CORS, if the client asks for a response header that is not among + the headers exposed by the Web application, the user agent may write an error + to the console, e.g., "REFUSED TO GET UNSAFE HEADER". This function checks for + a given response header in a way that will avoid the error message. It does this + by requesting all headers and then checking to see whether the desired header + is present (it will not be present, even if the server sent it, if the server has not + also allowed that header). The function caches the string returned by getAllResponseHeaders + by storing it on the xhr that was used in the request. It does the caching in + case there is another header to be checked. + */ + function getResponseHeaderNoError(xhr, headerName) { + var allHeaders = xhr._pdsResponseHeaders, + regExp; + + if (allHeaders === undefined) { + allHeaders = xhr.getAllResponseHeaders(); + if ( allHeaders ) { + xhr._pdsResponseHeaders = allHeaders; + } + else { + xhr._pdsResponseHeaders = null; + } + } + if ( allHeaders ) { + regExp = new RegExp("^" + headerName + ":", "m"); + if ( allHeaders.match(regExp) ) { + return xhr.getResponseHeader(headerName); + } + } + + return null; + } + + // "Methods" + + this._pushJSDOs = function (jsdo) { + _jsdos.push(jsdo); + }; + + + /* _openRequest (intended for progress.data library use only) + * calls open() for an xhr -- the assumption is that this is an xhr for a JSDO, and we need to add + * some session management information for the request, such as user credentials and a session ID if + * there is one + * + * The callback parameter is to support async calls --- it's possible that the call in here to + * _openRequestAndAuthorize will make an async request (for token refresh), so it's expected that + * callers will invoke _openRequest with a callback parameter for async execution + */ + this._openRequest = function (xhr, verb, url, async, callback) { + var urlPlusCCID, + that = this; + + function afterOpenAndAuthorize(xhr) { + // add CCID header + if (that.clientContextId && (that.clientContextId !== "0")) { + xhr.setRequestHeader("X-CLIENT-CONTEXT-ID", that.clientContextId); + } + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(that, xhr); + + if (typeof that.onOpenRequest === 'function') { + var params = { + "xhr": xhr, + "verb": verb, + "uri": urlPlusCCID, + "async": async, + "formPreTest": false, + "session": that + }; + that.onOpenRequest(params); + // xhr = params.xhr; //Note that, currently, this would have no effect in the caller. + } + if (callback) { + callback(); + } + } + + if (this._isInvalidated) { + // Session: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "Session")); + } + + if (this.loginResult !== progress.data.Session.LOGIN_SUCCESS && !this._authProvider && this.authenticationModel) { + throw new Error("Attempted to make server request when there is no active session."); + } + + // if resource url is not absolute, add the REST app url to the front + urlPlusCCID = this._prependAppURL(url); + + // add CCID as JSESSIONID query string to url + urlPlusCCID = this._addCCIDtoURL(urlPlusCCID); + + // add time stamp to the url + if (progress.data.Session._useTimeStamp) { + urlPlusCCID = progress.data.Session._addTimeStampToURL(urlPlusCCID); + } + + // should be able to remove this check and only do what's in the "if" when we no longer + // support calling the Session API directly (need to keep that now because tdriver, for + // one, uses the Session object, and uses it synchronously + if (this._authProvider) { + this._authProvider._openRequestAndAuthorize(xhr, + verb, + urlPlusCCID, + async, + afterOpenAndAuthorize); + } else { + this._setXHRCredentials(xhr, verb, urlPlusCCID, this.userName, _password, async); + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + _addWithCredentialsAndAccept(xhr, "application/json"); + } + afterOpenAndAuthorize(xhr); + } + + }; + + // callback used in login to determine whether ping is available on server + this.pingTestCallback = function (cbArgs) { + var foundOeping = cbArgs.pingResult ? true : false; + + setOepingAvailable(foundOeping); + }; + + // generic async callback, currently used by login(), addCatalog(), logout(), connect, and disconnect + this._onReadyStateChangeGeneric = function () { + var xhr = this; + var result; + var errorObject; + + clearTimeout(xhr._requestTimeout); // for the iOS Basic Auth bug + + if (xhr.readyState == 4) { + result = null; + errorObject = null; + + // initial processing of the response from the Web application + if ((typeof xhr.onResponseFn) == 'function') { + try { + result = xhr.onResponseFn(xhr); + // ( note that result will remain null if this is a logout() ) + } + catch (e) { + errorObject = e; + } + } + // handle the results of the processing (e.g., fire any events required) + if ((typeof xhr.onResponseProcessedFn) == 'function') { + if (!result) { + result = progress.data.Session.GENERAL_FAILURE; + } + xhr.onResponseProcessedFn(xhr.pdsession, result, errorObject, xhr); + } + } + }; + + // Intended only for internal use by the JSDO library + // NOTE: disconnect does not currently send a request to the Web application for the Anonymous or + // OE SSO models. It's conceivable, though unlikely, that it might. For that reason, the design is + // similar to the functions that DO make a server request. There is a "setup" function (this one) + // and a separate function to process the "result" (_processDisconnectResult, below). Currently the + // setup function is minimal and just calls _processDisconnectResult directly. If we ever do need to + // send a server request, _processDisconnectResult will be specified as the callback to be invoked + // from onReadyStateChangeGeneric. The possibility of this potential enhancement is the reason for + // the odd signature of _processDisconnectResult, which has a currently unused first parameter for + // the potential XHR. + this._disconnect = function (deferred) { + + // Note: we use the "no harm, no foul" approach for disconnect. If you aren't connected, it's + // regarded as a success rather than cause for throwing an error. + this._processDisconnectResult(null, deferred); + }; + + + // This is separate from _disconnect for cases in which _disconnect makes a server request. + // If there has been a server request, xhr should be valid and deferred will be undefined + // If there was no server request, xhr will be undefined and deferred will be valid. + // If this needs to be enhanced to support server requests, see _procesLoginResponse as + // a general model + // Probably the only time this function will be called as the result of a server request is with + // Form authentication, and even then it's questionable + this._processDisconnectResult = function (xhr, deferred) { + + this._reinitializeAfterLogout(this, progress.data.Session.SUCCESS); + this._disconnectComplete(this, progress.data.Session.SUCCESS, null, null, deferred); + }; + + this._disconnectComplete = function (pdsession, result, errObj, xhr, deferred) { + pdsession.trigger("afterDisconnect", pdsession, result, errObj, xhr, deferred); + }; + + + // GET RID OF progress.data.Session login CODE (AND RELATED) IF WE DROP SUPPORT FOR USING + // THE OLD progress.data.Session API DIRECTLY (mainly a problem for existing code (tdriver), + // or anyone who wants to call methods synchronously, which should be no one) + /* login + * + */ + + // store password here until successful login; only then do we store it in the Session object + var pwSave = null; + // store user name here until successful login; only then do we store it in the Session object + var unameSave = null; + this.login = function (serviceURI, loginUserName, loginPassword, loginTarget) { + var uname, + pw, + isAsync = false, + args = [], + deferred, + iOSBasicAuthTimeout, + uriForRequest; // "decorated" version of serviceURI, used to actually send the request + + pwSave = null; // in case these are left over from a previous login + unameSave = null; + + if (!defPropSupported) { + // this is here on the presumably slim chance that we're running with a + // version of JavaScript that doesn't support defineProperty (otherwise + // the lower casing will have already happened). When we decide that it's + // OK to remove our conditionalization of property definitions, we should + // get rid of this whole conditional + this.authenticationModel = this.authenticationModel.toLowerCase(); + } + + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // Session: Cannot call login() when authenticationModel is SSO. + // Please use the AuthenticationProvider object instead. + throw new Error(progress.data._getMsgText("jsdoMSG057", 'Session', 'login()')); + } + + if (this.loginResult === progress.data.Session.LOGIN_SUCCESS || this._authProvider) { + throw new Error("Attempted to call login() on a Session object that is already logged in."); + } + + if (arguments.length > 0) { + if (arguments[0] && typeof(arguments[0]) === 'object') { + // Note that arguments[0].serviceURI may be undefined because when the JSDOSession + // uses a Session internally, it passes serviceURI to the constructor. The other + // properties may be present, though + args[0] = arguments[0].serviceURI; + args[1] = arguments[0].userName; + args[2] = arguments[0].password; + args[3] = arguments[0].loginTarget; + args[4] = arguments[0].async; + + /* Special for JSDOSession: if this method was called by a JSDOSession object, + it passes deferred and jsdosession and we need to eventually attach them + to the XHR we use so that the promise created by the JSDOSession will work + correctly + */ + deferred = arguments[0].deferred; + + iOSBasicAuthTimeout = arguments[0].iOSBasicAuthTimeout; + if ( typeof iOSBasicAuthTimeout === 'undefined' ) { + iOSBasicAuthTimeout = defaultiOSBasicAuthTimeout; + } + else if (iOSBasicAuthTimeout && (typeof iOSBasicAuthTimeout != 'number')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'login', + 'The iOSBasicAuthTimeout argument was invalid.')); + } + } + else { + args = arguments; + } + } + + if (args.length > 0) { + if (args[0]) { + var restURLtemp = args[0]; + + // get rid of trailing '/' because appending service url that starts with '/' + // will cause request failures + if (restURLtemp[restURLtemp.length - 1] === "/") { + restURLtemp = restURLtemp.substring(0, restURLtemp.length - 1); + } + setServiceURI(restURLtemp, this); + } else if (!this.serviceURI) { + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); + throw new Error("Session.login() is missing the serviceURI argument."); + } + + if (args[1]) { + uname = args[1]; + } + + if (args[2]) { + pw = args[2]; + } + + if (args[3]) { + setLoginTarget(args[3], this); + } + + if (args[4]) { + if (typeof(args[4]) === 'boolean') { + isAsync = args[4]; + } + else { + throw new Error("Session.login() was passed an async setting that is not a boolean."); + } + } + } + else { + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); + throw new Error("Session.login() is missing the serviceURI argument."); + } + + // use these temp cred variables later; if login succeeds, we'll use them to set the + // real credentials + unameSave = uname; + pwSave = pw; + + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_ANON || + this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + /* anonymous should NOT have a username and password passed (this is + probably unnecessary because the XHR seems to send the request without + credentials first, then intercept the 401 if there is one and try again, + this time with credentials. Just making sure. + */ + /* For form authentication, we may as well not send the user name and password + * on this request, since we are just trying to test whether the authentication + * has already happened and they are therefore irrelevant + */ + uname = null; + pw = null; + } + + var xhr = new XMLHttpRequest(); + xhr.pdsession = this; + + try { + uriForRequest = this.serviceURI + this.loginTarget; + if (progress.data.Session._useTimeStamp) { + uriForRequest = progress.data.Session._addTimeStampToURL(uriForRequest); + } + this._setXHRCredentials(xhr, 'GET', uriForRequest, uname, pw, isAsync); + + progress.data.Session._setNoCacheHeaders(xhr); + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(this, xhr); + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + _addWithCredentialsAndAccept(xhr, + "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + } + + xhr._isAsync = isAsync; + if (isAsync) { + xhr.onreadystatechange = this._onReadyStateChangeGeneric; + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + xhr.onResponseFn = this._afterFormPretestLogin; + } + else { + xhr.onResponseFn = this._processLoginResult; + xhr.onResponseProcessedFn = this._loginComplete; + } + if ( this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC + && isUserAgentiOS + && iOSBasicAuthTimeout > 0 ) { + xhr._requestTimeout = setTimeout( function (){ + clearTimeout(xhr._requestTimeout); + xhr._iosTimeOutExpired = true; + xhr.abort(); + }, + iOSBasicAuthTimeout); + } + xhr._jsdosession = jsdosession; // in case the caller is a JSDOSession + xhr._deferred = deferred; // in case the caller is a JSDOSession + } + + if (typeof this.onOpenRequest === 'function') { + var isFormPreTest = false; + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + isFormPreTest = true; + } + + // set this here in case onOpenRequest checks it + setLastSessionXHR(xhr, this); + var params = { + "xhr": xhr, + "verb": "GET", + "uri": this.serviceURI + this.loginTarget, + "async": false, + "formPreTest": isFormPreTest, + "session": this + }; + this.onOpenRequest(params); + xhr = params.xhr; // just in case it has been changed + } + setLastSessionXHR(xhr, this); + xhr.send(null); + } + catch (e) { + clearTimeout(xhr._requestTimeout); + setLoginHttpStatus(xhr.status, this); + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); + unameSave = null; + pwSave = null; + throw e; + } + + if (isAsync) { + return progress.data.Session.ASYNC_PENDING; + } + else { + setLoginHttpStatus(xhr.status, this); + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + return (this._afterFormPretestLogin(xhr) ); + } + else { + return (this._processLoginResult(xhr) ); + } + } + }; + + + this._afterFormPretestLogin = function (xhr) { + var pdsession = xhr.pdsession; + setLoginHttpStatus(xhr.status, xhr.pdsession); + + var formLoginParams = { + "xhr": xhr, + "pw": pwSave, + "uname": unameSave, + "theSession": pdsession + }; + try { + return doFormLogin(formLoginParams); + } + catch (e) { + pwSave = null; + unameSave = null; + throw e; + } + }; + + /* doFormLogin + * This function handles logging in to a service that uses form-based authentication. It's separate + * from the main login function because it's long. One of the things it does is examine the + * response from an initial attempt to get the login target without credentials (done in the main + * login() function) to determine whether the user has already been authenticated. Although a + * current OE Mobile Web application (as of 5/30/2013) will return an error if authentication + * failed on a form login, previous versions and non-OE servers return a + * redirect to a login page and the user agent (browser or native wrapper) + * usually then fetches the redirect location and returns it along with a + * 200 Success status, when in fcat it was an authentication failure. Hence + * the need to analyze the response to try to figure out what we get back. + * + */ + function doFormLogin(args) { + var xhr = args.xhr; + var theSession = args.theSession; + var oldXHR; + + // check whether we got the OE REST Form based error response + var contentType = null; + var needAuth = false; + var params = { + "session": theSession, + "xhr": xhr, + "statusFromjson": null + }; + + contentType = xhr.getResponseHeader("Content-Type"); + + if (contentType && contentType.indexOf("application/json") >= 0) { + handleJSONLoginResponse(params); + if ( !params.statusFromjson + || (params.statusFromjson >= 400 && params.statusFromjson < 500) + ) { + needAuth = true; + } + else { + // either the response shows that we're already authenticated, or + // there's some error other than an authentication error + setLoginHttpStatus(params.statusFromjson, theSession); + } + } + else { + // need to do only 200 for async to work with MWA down + if (theSession.loginHttpStatus == 200) { + if (_gotLoginForm(xhr)) { + needAuth = true; + } + // else we are assuming we truly retrieved the login target and + // therefore we were previously authenticated + } + // else had an error, just return it + } + + if (needAuth) { + // create new XHR, because if this is an async call we don't want to + // confuse things by using this xhr to send another request while we're + // still processing its old request (this function, doFormLogin(), may + // have been called from onReadyStateChangeGeneric and it's conceivable + // that that function has more code to execute involving this xhr) + oldXHR = xhr; + xhr = new XMLHttpRequest(); + args.xhr = xhr; + params.xhr = xhr; + + // need to transfer any properties that the Session code stored in the + // the xhr that need to persist across the 2 requests made by a our + // login implementation for Form auth + xhr.pdsession = oldXHR.pdsession; + xhr._isAsync = oldXHR._isAsync; + xhr._deferred = oldXHR._deferred; // special for JSDOSession + xhr._jsdosession = oldXHR._jsdosession; // special for JSDOSession + + xhr.open('POST', theSession.serviceURI + "/static/auth/j_spring_security_check",xhr._isAsync); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + xhr.setRequestHeader("Cache-Control", "max-age=0"); + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(theSession, xhr); + + _addWithCredentialsAndAccept(xhr, "application/json"); + + try { + + // Note: this gives a developer a way to change certain aspects of how we do the + // form-based login, but we will still be assuming that we are going directly to + // j_spring_security_check and including credentials in the body. They really should not + // try to change that. + // + if (typeof theSession.onOpenRequest === 'function') { + var cbparams = { + "xhr": xhr, + "verb": "POST", + "uri": theSession.serviceURI + "/static/auth/j_spring_security_check", + "async": xhr._isAsync, + "formPreTest": false, + "session": theSession + }; + theSession.onOpenRequest(cbparams); + xhr = cbparams.xhr; + } + + if (xhr._isAsync) { + xhr.onreadystatechange = theSession._onReadyStateChangeGeneric; + xhr.onResponseFn = theSession._afterFormLogin; + xhr.onResponseProcessedFn = theSession._loginComplete; + } + + // j_username=username&j_password=password&submit=Submit + xhr.send("j_username=" + encodeURIComponent(args.uname) + "&j_password=" + encodeURIComponent(args.pw) + "&submit=Submit"); + } + catch (e) { + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, theSession); + setLoginHttpStatus(xhr.status, theSession); + // null the temporary credentials variables + unameSave = null; + pwSave = null; + throw e; + } + + } + + if (xhr._isAsync && !needAuth) { + xhr.onResponseProcessedFn = theSession._loginComplete; + return theSession._afterFormLogin(xhr); + } + if (!xhr._isAsync) { + return theSession._afterFormLogin(xhr); + } + + } + + this._afterFormLogin = function (xhr) { + // check what we got + var theSession = xhr.pdsession; + var params = { + "session": theSession, + "xhr": xhr, + "statusFromjson": null + }; + var contentType = xhr.getResponseHeader("Content-Type"); + + if (contentType && contentType.indexOf("application/json") >= 0) { + handleJSONLoginResponse(params); + if (!params.statusFromjson) { + throw new Error( + "Internal OpenEdge Mobile client error handling login response. HTTP status: " + + xhr.status + "."); + } + else { + setLoginHttpStatus(params.statusFromjson, theSession); + } + } + else { + if (xhr.status === 200) { + // Was the response actually the login failure page or the login page itself (in case + // the appSecurity config file sets the login failure url so the server sends the login + // page again)? If so, call it an error because the credentials apparently failed to be + // authenticated + if (_gotLoginFailure(xhr) || _gotLoginForm(xhr)) { + setLoginHttpStatus(401, theSession); + } + else { + setLoginHttpStatus(xhr.status, theSession); + } + } + } + + return theSession._processLoginResult(xhr); + }; + + + this._processLoginResult = function (xhr) { + /* OK, one way or another, by hook or by crook, the Session object's loginHttpStatus + * has been set to the value that indicates the real outcome of the + * login, after adjusting for form-based authentication and anything + * else. At this point, it should be just a matter of examining + * this.loginHttpStatus, using it to set this.loginResult, maybe doing + * some other work appropriate to the outcome of the login, and returning + * this.loginResult. + */ + var pdsession = xhr.pdsession; + + setLoginHttpStatus(xhr.status, xhr.pdsession); + + if (pdsession.loginHttpStatus === 200) { + setLoginResult(progress.data.Session.LOGIN_SUCCESS, pdsession); + setRestApplicationIsOnline(true); + setUserName(unameSave, pdsession); + _password = pwSave; + pdsession._saveClientContextId(xhr); + storeAllSessionInfo(); // save info to persistent storage + + var pingTestArgs = { + pingURI: null, async: true, onCompleteFn: null, + fireEventIfOfflineChange: true, onReadyStateFn: pdsession._pingtestOnReadyStateChange + }; + pingTestArgs.pingURI = pdsession._makePingURI(); + pdsession._sendPing(pingTestArgs); // see whether the ping feature is available + } + else { + if (pdsession.loginHttpStatus == 401) { + setLoginResult(progress.data.Session.LOGIN_AUTHENTICATION_FAILURE, pdsession); + } + else { + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, pdsession); + } + } + setLastSessionXHR(xhr, pdsession); + updateContextPropsFromResponse(pdsession, xhr); + + // null the temporary credentials variables + unameSave = null; + pwSave = null; + if (xhr._iosTimeOutExpired) { + throw new Error( progress.data._getMsgText("jsdoMSG047", "login") ); + } + + // return loginResult even if it's an async operation -- the async handler + // (e.g., onReadyStateChangeGeneric) will just ignore + return pdsession.loginResult; + }; + + + this._loginComplete = function (pdsession, result, errObj, xhr) { + pdsession.trigger("afterLogin", pdsession, result, errObj, xhr); + }; + + // GET RID OF progress.data.Session logout CODE (AND RELATED) IF WE DROP SUPPORT FOR USING + // THE OLD progress.data.Session API DIRECTLY (mainly a problem for existing code (tdriver), + // or anyone who wants to call methods synchronously, which should be no one) + this.logout = function (args) { + var isAsync = false, + errorObject = null, + xhr, + deferred, + params; + + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // Session: Cannot call logout() when authenticationModel is SSO. + // Please use the AuthenticationProvider object instead. + throw new Error(progress.data._getMsgText("jsdoMSG057", 'Session', 'logout()')); + } + + if (this.loginResult !== progress.data.Session.LOGIN_SUCCESS && this.authenticationModel) { + throw new Error("Attempted to call logout when there is no active session."); + } + + if (typeof(args) === 'object') { + isAsync = args.async; + if (isAsync && (typeof isAsync !== 'boolean')) { + throw new Error( progress.data._getMsgText("jsdoMSG033", + "Session", + 'logout', + 'The async argument was invalid.')); + } + /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes + deferred and jsdosession and we need to eventually attach them to the XHR we use + so that the promise created by the JSDOSession will work correctly + */ + deferred = args.deferred; + } + + xhr = new XMLHttpRequest(); + xhr.pdsession = this; + try { + /* logout when auth model is anonymous is a no-op on the server side + (but we need to set _jsdosession and _deferred anyway to make promise work + if logout was called by a JSDOSession) */ + xhr._jsdosession = jsdosession; // in case the caller is a JSDOSession + xhr._deferred = deferred; // in case the caller is a JSDOSession + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM || + this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { + if (isAsync) { + xhr.onreadystatechange = this._onReadyStateChangeGeneric; + xhr.onResponseFn = this._processLogoutResult; + xhr.onResponseProcessedFn = this._logoutComplete; + } + + + xhr.open('GET', this.serviceURI + "/static/auth/j_spring_security_logout", isAsync); + + /* instead of calling _addWithCredentialsAndAccept, we code the withCredentials + * and setRequestHeader inline so we can do it slightly differently. That + * function deliberately sets the request header inside the try so we don't + * run into a FireFox oddity that would give us a successful login and then + * a failure on getCatalog (see the comment on that function). On logout, + * however, we don't care -- just send the Accept header so we can get a 200 + * response + */ + try { + xhr.withCredentials = true; + } + catch (e) { + } + + xhr.setRequestHeader("Accept", "application/json"); + + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(this, xhr); + + if (typeof this.onOpenRequest === 'function') { + setLastSessionXHR(xhr, this); + params = { + "xhr": xhr, + "verb": "GET", + "uri": this.serviceURI + "/static/auth/j_spring_security_logout", + "async": false, + "formPreTest": false, + "session": this + }; + this.onOpenRequest(params); + xhr = params.xhr; + } + + setLastSessionXHR(xhr, this); + xhr.send(); + } + else { + xhr._anonymousLogoutOK = true; + } + } + catch (e) { + this._reinitializeAfterLogout(this, false); + throw e; + } + + if (!isAsync) { + try { + this._processLogoutResult(xhr); + } + catch (e) { + throw e; + } + } + + if (isAsync && this.authenticationModel === progress.data.Session.AUTH_TYPE_ANON) { + // fake async for Anonymous -- fire afterLogout event + try { + this._processLogoutResult(xhr); + } + catch (e) { + errorObject = e; + } + this._logoutComplete(this, null, errorObject, xhr); + } + + }; + + // This function erases all evidence of itself from the ServicesManager and + // flips a bit to prevent it to be used in the future + this.invalidate = function () { + isInvalidated = true; + cleanServicesManager(); + }; + + this._logoutComplete = function (pdsession, result, errorObject, xhr) { + // ignore result, it doesn't apply to logout -- is probably null or GENERAL_FAILURE + // we include it so onReadyStateChangeGeneric calls this correctly + pdsession.trigger("afterLogout", pdsession, errorObject, xhr); + }; + + this._processLogoutResult = function (xhr) { + var logoutSucceeded; + var pdsession = xhr.pdsession; + var basicStatusOK = false; + + if (xhr._anonymousLogoutOK) { + logoutSucceeded = true; + } + else if (xhr.status !== 200) { + /* Determine whether an error returned from the server is really an error + */ + if (pdsession.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { + /* If the Auth model is Basic, we probably got back a 404 Not found. + * But that's OK, because logout from Basic is meaningless on the + * server side unless it happens to be stateful, which is the only + * reason we even try calling j_spring_security_logout + */ + if (xhr.status === 404) { + logoutSucceeded = true; + } + else { + logoutSucceeded = false; + throw new Error("Error logging out, HTTP status = " + xhr.status); + } + } + else { + // for Form auth, any error on logout is an error + logoutSucceeded = false; + + // page refresh - we should call _reinitializeAfterLogout, or do something, so that + // caller can try logging in again (this is not a problem specific to page refresh, + // but the case of a page refresh after a server has gone down emphasizes it) + + throw new Error("Error logging out, HTTP status = " + xhr.status); + } + } + else { + logoutSucceeded = true; + } + + updateContextPropsFromResponse(pdsession, xhr); + pdsession._reinitializeAfterLogout(pdsession, logoutSucceeded); + }; + + this._reinitializeAfterLogout = function (pdsession, success) { + setLoginResult(null, pdsession); + setLoginHttpStatus(null, pdsession); + setClientContextID(null, pdsession); + setUserName(null, pdsession); + _password = null; + setAuthProvider(null); + + if (success) { + setRestApplicationIsOnline(false); + setOepingAvailable(false); + setPartialPingURI(defaultPartialPingURI); + setLastSessionXHR(null, pdsession); + clearTimeout(_timeoutID); // stop autopinging + } + }; + + + /* addCatalog + * + */ + this.addCatalog = function (arg1, arg2, arg3, arg4) { + var catalogURI, + catalogUserName, + catalogPassword, + isAsync = false, + xhr, + deferred, + iOSBasicAuthTimeout, + catalogIndex, + authProvider, + that = this; + + function addCatalogAfterOpen() { + /* This is here as much for CORS situations as the possibility that there might be an + * out of date cached version of the catalog. The CORS problem happens if you have + * accessed the catalog locally and then run an app on a different server that requests + * the catalog. Your browser already has the catalog, but the request used to get it was + * a non-CORS request and the browser will raise an error + */ + progress.data.Session._setNoCacheHeaders(xhr); + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(that, xhr); + + if (isAsync) { + xhr.onreadystatechange = that._onReadyStateChangeGeneric; + xhr.onResponseFn = that._processAddCatalogResult; + xhr.onResponseProcessedFn = that._addCatalogComplete; + + if (that.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC + && isUserAgentiOS + && iOSBasicAuthTimeout) { + xhr._requestTimeout = setTimeout(function () { + clearTimeout(xhr._requestTimeout); + xhr._iosTimeOutExpired = true; + xhr.abort(); + }, + iOSBasicAuthTimeout); + } + + // in case the caller is a JSDOSession + xhr._jsdosession = jsdosession; + xhr._deferred = deferred; + xhr._catalogIndex = catalogIndex; + } + + try { + if (typeof that.onOpenRequest === 'function') { + setLastSessionXHR(xhr, that); + var params = { + "xhr": xhr, + "verb": "GET", + "uri": catalogURI, + "async": false, + "formPreTest": false, + "session": that + }; + that.onOpenRequest(params); + xhr = params.xhr; + } + + setLastSessionXHR(xhr, that); + xhr.send(null); + } catch (e) { + throw new Error("Error retrieving catalog '" + catalogURI + "'.\n" + e.message); + } + if (isAsync) { + return progress.data.Session.ASYNC_PENDING; + } else { + return that._processAddCatalogResult(xhr); + } + + } + + if (this._isInvalidated) { + // JSDOSession: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); + } + + // Assume we're using a custom username/pw/authprovider + customCredentials = true; + + // check whether the args were passed in a single object. If so, copy them + // to the named arguments and a variable + if (arguments.length > 0) { + if (typeof arg1 === 'object') { + // check whether it's OK to add a catalog whilst offline + if (!arguments[0].offlineAddCatalog) { + if ((this.loginResult !== progress.data.Session.LOGIN_SUCCESS + && !this._authProvider) + && this.authenticationModel) { + throw new Error("Attempted to call addCatalog when there is no active session."); + } + } + + catalogURI = arg1.catalogURI; + if (!catalogURI || (typeof catalogURI !== 'string')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', + 'The catalogURI argument was missing or invalid.')); + } + catalogUserName = arg1.userName; + if (catalogUserName && (typeof catalogUserName !== 'string')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', + 'The catalogUserName argument was invalid.')); + } + catalogPassword = arg1.password; + if (catalogPassword && (typeof catalogPassword !== 'string')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', + 'The catalogPassword argument was invalid.')); + } + isAsync = arg1.async; + if (isAsync && (typeof isAsync !== 'boolean')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', + 'The async argument was invalid.')); + } + iOSBasicAuthTimeout = arg1.iOSBasicAuthTimeout; + if (typeof iOSBasicAuthTimeout === 'undefined') { + iOSBasicAuthTimeout = defaultiOSBasicAuthTimeout; + } else if (iOSBasicAuthTimeout && (typeof iOSBasicAuthTimeout !== 'number')) { + throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', + 'The iOSBasicAuthTimeout argument was invalid.')); + } + authProvider = arg1.authProvider; + + /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes + deferred, jsdosession, and catalogIndex and we need to eventually attach them to the + XHR we use so that the promise created by the JSDOSession will work correctly + */ + deferred = arg1.deferred; + catalogIndex = arg1.catalogIndex; + } else { + catalogURI = arg1; + if (typeof catalogURI !== 'string') { + throw new Error("First argument to Session.addCatalog must be the URL of the catalog."); + } + catalogUserName = arg2; + if (catalogUserName && (typeof catalogUserName !== 'string')) { + throw new Error("Second argument to Session.addCatalog must be a user name string."); + } + catalogPassword = arg3; + if (catalogPassword && (typeof catalogPassword !== 'string')) { + throw new Error("Third argument to Session.addCatalog must be a password string."); + } + } + } else { + throw new Error("Session.addCatalog is missing its first argument, the URL of the catalog."); + } + + if (!authProvider) { + authProvider = this._authProvider; + + // Guess we're using the default credentials passed earlier + customCredentials = false; + } + + // TODO: we expect that there will always be an authProvider if a login has been done. + // Therefore, we don't need to set catalogUsername and catalogPassword if they aren't + // passed in. What we should do here, when we extend the AuthenticationProvider API + // for the older auth models, is take any uname and pw passed in and create an auth + // provider, log in to the catalogURI with it, create an authImpl, and then fetch the + // catalog. + if (!catalogUserName) { + catalogUserName = this.userName; + } + + if (!catalogPassword) { + catalogPassword = _password; + } + + xhr = new XMLHttpRequest(); + xhr.pdsession = this; + xhr._catalogURI = catalogURI; + + // for now we don't support multiple version of the catalog across sessions + if (progress.data.ServicesManager.getSession(catalogURI) !== undefined) { + if (isAsync) { + /* + Attempt to get the event to fire AFTER this call returns ASYNC_PENDING + (and if the method was called from a JSDOSession, create an xhr to communicate + information related to promises back to its afterAddCatalog handler). Note that + the xhr is never used to make a request, it just carries data in the way + expected by the handler) + */ + // in case the caller is a JSDOSession + xhr._jsdosession = jsdosession; + xhr._deferred = deferred; + xhr._catalogIndex = catalogIndex; + + setTimeout(this._addCatalogComplete, 10, this, + progress.data.Session.CATALOG_ALREADY_LOADED, null, xhr); + return progress.data.Session.ASYNC_PENDING; + } + return progress.data.Session.CATALOG_ALREADY_LOADED; + } + + if (authProvider) { + authProvider._openRequestAndAuthorize(xhr, 'GET', catalogURI, isAsync, addCatalogAfterOpen); + // existing code in JSDOSession addCatalog expects to get this as a return value, + // have to return it now + return progress.data.Session.ASYNC_PENDING; + } else { // should be able to get rid of this if we do away with synchronous (old Session API) support + this._setXHRCredentials(xhr, 'GET', catalogURI, catalogUserName, catalogPassword, isAsync); + // Note that we are not adding the CCID to the URL or as a header, because the catalog may not + // be stored with the REST app and even if it is, the AppServer ID shouldn't be relevant + + return addCatalogAfterOpen(); + } + + }; + + this._processAddCatalogResult = function (xhr) { + var _catalogHttpStatus = xhr.status; + var theSession = xhr.pdsession; + var servicedata; + var catalogURI = xhr._catalogURI, + serviceURL, + theJSDOSession = jsdosession; + + // Only change the Session's state if the default AuthProv is being used + if (!customCredentials) { + toggleOnlineState(xhr); + } + + if ((_catalogHttpStatus == 200) || (_catalogHttpStatus === 0) && xhr.responseText) { + servicedata = theSession._parseCatalog(xhr); + try { + progress.data.ServicesManager.addCatalog(servicedata, theSession); + } + catch (e) { + if (progress.data.ServicesManager.getSession(catalogURI) !== undefined) { + /* this failed because the catalog had already been loaded, but the code + in addCatalog did not catch that, probably because we are executing + the JSDOSession addCatalog with multiple catalogURIs passed, and 2 + are the same + */ + return progress.data.Session.CATALOG_ALREADY_LOADED; + } + // different catalogs, with same resource name + throw new Error("Error processing catalog '" + catalogURI + "'. \n" + e.message); + } + // create a mobile service object and add it to the Session's array of same + for (var i = 0; i < servicedata.length; i++) { + serviceURL = theSession._prependAppURL(servicedata[i].address); + pushService(new progress.data.MobileServiceObject( + { + name: servicedata[i].name, + uri: serviceURL + }), + theSession); + + if (servicedata[i].settings + && servicedata[i].settings.useXClientProps + && !theSession.xClientProps) { + console.warn("Catalog warning: Service settings property 'useXClientProps' " + + "is true but 'xClientProps' property has not been set."); + } + } + pushCatalogURIs(catalogURI, theSession); + progress.data.ServicesManager.addSession(catalogURI, theSession); + if (theJSDOSession) { + progress.data.ServicesManager.addJSDOSession(catalogURI, theJSDOSession); + } + } + else if (_catalogHttpStatus == 401) { + return progress.data.AuthenticationProvider._getAuthFailureReason(xhr); + } + else if (xhr._iosTimeOutExpired) { + throw new Error( progress.data._getMsgText("jsdoMSG047", "addCatalog") ); + } + else { + throw new Error("Error retrieving catalog '" + catalogURI + + "'. Http status: " + _catalogHttpStatus + "."); + } + + return progress.data.Session.SUCCESS; + }; + + this._addCatalogComplete = function (pdsession, result, errObj, xhr) { + pdsession.trigger("afterAddCatalog", pdsession, result, errObj, xhr); + }; + + + /* + * ping -- determine whether the Mobile Web Application that the Session object represents + * is available, which includes determining whether its associated AppServer is running + * Also determine whether the Mobile services managed by this Session object are available + * (which means simply that they're known to the Mobile Web Application) + * (Implementation note: be sure that this Session object's "connected" + * property retains its current value until the end of this function, where + * it gets updated, if necessary, after calling _isOnlineStateChange + * + * Signatures : + * @param arg + * There are 2 signatures -- + * - no argument -- do an async ping of the Session's Mobile Web application. The only effect + * of the ping will be firing an offline or an online event, if appropriate + * The ping function itself will return false to the caller + * - object argument -- the object's properties provide the input args. They are all + * optional (if for some reason the caller passes an object that has no properties, it's + * the same as passing no argument at all). The properties may be: + * async -- tells whether to execute the ping asynchronously (which is the default) + * onCompleteFn -- if async, this will be called when response returns + * doNotFireEvent -- used internally, controls whether the ping method causes an offline + * or online event to be fired if there has been a change (the default is that it + * does, but our Session._checkServiceResponse() sets this to true so that it can + * control the firing of the event) + * offlineReason -- if present, and if the ping code discovers that teh server is offline, + * the ping code will set this with its best guess + * as to the reason the server is offline + */ + this.ping = function (args) { + var pingResult = false; + var pingArgs = { + pingURI: null, async: true, onCompleteFn: null, + fireEventIfOfflineChange: true, onReadyStateFn: this._onReadyStateChangePing, + offlineReason: null + }; + + if (this._isInvalidated) { + // Session: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "Session")); + } + + if ((!this._authProvider) && (this.loginResult !== progress.data.Session.LOGIN_SUCCESS)) { + throw new Error("Attempted to call ping when not logged in."); + } + + if (args) { + if (args.async !== undefined) { + // when we do background pinging (because pingInterval is set), + // we pass in an arg that is just an object that has an async property, + // set to true. This can be expanded to enable other kinds of ping calls + // to be done async (so that application developers can do so, if we decide + // to support that) + pingArgs.async = args.async; + } + + if (args.doNotFireEvent !== undefined) { + pingArgs.fireEventIfOfflineChange = !args.doNotFireEvent; + } + + if (args.onCompleteFn && (typeof args.onCompleteFn) == 'function') { + pingArgs.onCompleteFn = args.onCompleteFn; + } + /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes + deferred and jsdosession and we need to eventually attach them to the XHR we use so that + the promise created by the JSDOSession will work correctly + */ + pingArgs.deferred = args.deferred; + pingArgs.jsdosession = args.jsdosession; + + } + + + /* Ping the Mobile Web Application (this will also determine whether AppServer is available) + * Call _processPingResult() if we're synchronous, otherwise the handler for the xhr.send() + * will call it + */ + pingArgs.pingURI = that._makePingURI(); + that._sendPing(pingArgs); + if (!pingArgs.async) { + if (pingArgs.xhr) { + pingResult = that._processPingResult(pingArgs); + if (args.offlineReason !== undefined) { + args.offlineReason = pingArgs.offlineReason; + } + } + else { + pingResult = false; // no xhr returned from _sendPing, something must have gone wrong + } + if ( args.xhr !== undefined ) { + // if it's a sync ping, return the xhr if caller indicates they want it + // (there's almost guaranteed to be one, even if the ping was never sent + // if for some reason there isn't, we give them the null or undefined we ended up with) + args.xhr = pingArgs.xhr; + } + } + // else it's async, deliberately returning false + // so developer not misled into thinking the ping succeeded + + return pingResult; + }; + + + // "protected" Functions + + /* + * given a value of true or false for being online for the Mobile Web Application + * managed by this Session object, determine whether that changes the current + * state of being offline or online. + * Returns true if the input state is a change from the current state + * + * Signature : + * @param isOnline Required. True to determine whether online is a state change, false to + * determine whether offline constitutes a state change. Boolean. + * + */ + this._isOnlineStateChange = function (isOnline) { + var stateChanged = false; + + if (isOnline && !(this.connected)) { + stateChanged = true; + } + else if (!isOnline && ( this.connected )) { + stateChanged = true; + } + + return stateChanged; + }; + + + /* + * given information about the response from a request made to a service, + * do the following: + * + * determine whether the online status of the Session has changed, and + * set the Session's Connected property accordingly + * if the Session's online status has changed, fire the appropriate event + * + * Signature : + * @param xhr Required. The xhr that was used to make the request. Object + * @param success Required. True if caller regards the request as having succeeded. Boolean + * @param request Required. The JSDO request object created for making the request. Object. + * + */ + this._checkServiceResponse = function (xhr, success, request) { + var offlineReason = null, + wasOnline = this.connected; + updateContextPropsFromResponse(this, xhr); + + /* first of all, if there are no subscriptions to offline or online events, don't + * bother -- we don't want to run the risk of messing things up by calling ping + * if the app developer isn't interested (especially because that may mean that + * ping isn't enabled on the server, anyway) + */ + if (!this._events) { + return; + } + var offlineObservers = this._events["offline"] || []; + var onlineObservers = this._events["online"] || []; + if ((offlineObservers.length === 0) && (onlineObservers.length === 0)) { + return; + } + + /* even though this function gets called as a result of trying to + * contact the server, don't bother to change anything if we already + * know that the device (or user agent, or client machine) is offline. + * We can't assume anything about the state of the server if we can't + * even get to the internet from the client + */ + + // if the call to the server was a success, we will assume we are online, + // both server and device + if (success) { + setRestApplicationIsOnline(true); + setDeviceIsOnline(true); // presumably this is true (probably was already true) + } + else { + /* Request failed, determine whether it's because server is offline + * Do this even if the Session was already in an offline state, because + * we need to determine whether the failure was due to still being + * offline, or whether it's now possible to communicate with the + * server but the problem was something else. + */ + + if (deviceIsOnline) { + /* ping the server to get better information on whether this is an offline case + * NB: synchronous ping for simplicity, maybe should consider async so as not + * to potentially freeze UI + */ + var localPingArgs = { + doNotFireEvent: true, // do in this fn so we have the request + offlineReason: null, + async: false + }; + if (!(that.ping(localPingArgs) )) { + offlineReason = localPingArgs.offlineReason; + setRestApplicationIsOnline(false); + } + else { + // ping returned true, so even though the original request failed, + // we are online and the failure must have been due to something else + setRestApplicationIsOnline(true); + } + } + // else deviceIsOnline was already false, so the offline event should already have + // been fired for that reason and there is no need to do anything else + } + + if (wasOnline && !this.connected) { + this.trigger("offline", this, offlineReason, request); + } + else if (!wasOnline && this.connected) { + this.trigger("online", this, request); + } + }; + + /* Decide whether, on the basis of information returned by a server request, the + * Mobile Web Application managed by this Session object is online, where online + * means that the ping response was a 200 and, IF the body of the response contains + * JSON with an AppServerStatus property, that AppServerStatus Status property has + * a pingStatus property set to true + * i.e., the body has an AppServerStatus.PingStatus set to true + * (if the body doesn't contain JSON with an AppServerStatus, we use just the HTTP + * response status code to decide) + * + * Returns: true if the response meets the above conditions, false if it doesn't + * + * Parameters: + * args, with properties: + * xhr - the XMLHttpRequest used to make the request + * offlineReason - if the function determines that the app is offline, + * it sets offlineReason to the reason for that decision, + * for the use of the caller + * fireEventIfOfflineChange - if true, the function fires an offline or online + * event if there has been a change (i.e., the online state determined + * by the function is different from what it had been when the function + * began executing) + * usingOepingFormat - OPTIONAL. The function's default assumption is that the value + * of the session's internal oepingAvailable variable indicates whether the + * the response body will be in the format used by the OpenEdge oeping service. + * A caller can override this assumption by using this property to true or false. + * (the isAuthorized code sets this to false because it doesn't use oeping + * but does call this function) + */ + this._processPingResult = function (args) { + var xhr = args.xhr, + pingResponseJSON, + appServerStatus = null, + wasOnline = this.connected, + connectedBeforeCallback, + assumeOepingFormat; + + if (args.hasOwnProperty('usingOepingFormat')) { + assumeOepingFormat = args.usingOepingFormat; + } else { + assumeOepingFormat = oepingAvailable; + } + + /* first determine whether the Web server and the Mobile Web Application (MWA) + * are available + */ + if (xhr.status >= 200 && xhr.status < 300) { + updateContextPropsFromResponse(this, xhr); + if (assumeOepingFormat) { + try { + pingResponseJSON = JSON.parse(xhr.responseText); + appServerStatus = pingResponseJSON.AppServerStatus; + } + catch (e) { + /* We got a successful response from calling our ping URI, but it + * didn't return valid JSON. If we think that the oeping REST API + * is available on the server (so we should have gotten valid + * json), log this to the console. + * + */ + console.error("Unable to parse ping response."); + } + } + toggleOnlineState(xhr); + } + else { + if (deviceIsOnline) { + if (xhr.status === 0) { + args.offlineReason = progress.data.Session.SERVER_OFFLINE; + setRestApplicationIsOnline(false); + } + else if ((xhr.status === 404) || (xhr.status === 410)) { + /* if we get a 404, it means the Web server is up, but it + * can't find the resource we requested (either _oeping or + * the login target), therefore the Mobile Web application + * must be unavailable (410 is Gone) + */ + args.offlineReason = progress.data.Session.WEB_APPLICATION_OFFLINE; + setRestApplicationIsOnline(false); + } + else { + /* There's some error, but we can't say for sure that it's because + * the Web application is unavailable. May be an authentication problem, + * internal server error, or for some reason our ping request was + * invalid (unlikely to happen if it previously succeeded). + * In particular, if the server uses Form authentication, it + * may have come back online but now the session id + * is no longer valid. + */ + setRestApplicationIsOnline(true); + } + } + else { + args.offlineReason = progress.data.Session.DEVICE_OFFLINE; + } + } + + // is the AppServer online? appServerStatus will be non-null only + // if the ping request returned 200, meaning the other things are OK + // (connection to server, Tomcat, Mobile Web application) + if (appServerStatus) { + if (appServerStatus.PingStatus === "false") { + args.offlineReason = progress.data.Session.APPSERVER_OFFLINE; + setRestApplicationIsOnline(false); + } + else { + setRestApplicationIsOnline(true); + } + } + + /* We call any async ping callback handler and then, after that returns, fire an + offline or online event if necessary. + When deciding whether to fire an event, the responsibility of this _processPingResult() + function is to decide about the event on the basis of the data returned from the ping + that it is currently processing. Therefore, since the ping callback that is just about + to be called could change the outcome of the event decision (for example, if the handler + calls logout(), thus setting Session.connected to false)), we save the current value of + Session.connected and use that saved value to decide about the event after the ping + handler returns. + (If the application programmer wants to get an event fired as a result of something + that happens in the ping handler, they should call a ping() *after* that. + */ + connectedBeforeCallback = this.connected; + + if ((typeof xhr.onCompleteFn) == 'function') { + xhr.onCompleteFn({ + pingResult: this.connected, + xhr: xhr, + offlineReason: args.offlineReason + }); + } + + // decide whether to fire an event, and if so do it + if (args.fireEventIfOfflineChange) { + if (wasOnline && !connectedBeforeCallback) { + that.trigger("offline", that, args.offlineReason, null); + } + else if (!wasOnline && connectedBeforeCallback) { + that.trigger("online", that, null); + } + } + + return this.connected; + }; + + + this._onReadyStateChangePing = function () { + var xhr = this; + var args; + + if (xhr.readyState == 4) { + args = { + xhr: xhr, + fireEventIfOfflineChange: true, + offlineReason: null + }; + that._processPingResult(args); + if (_pingInterval > 0) { + _timeoutID = setTimeout(that._autoping, _pingInterval); + } + } + }; + + this._pingtestOnReadyStateChange = function () { + var xhr = this; + + if (xhr.readyState == 4) { + var foundOeping = false; + if (xhr.status >= 200 && xhr.status < 300) { + foundOeping = true; + } + else { + setPartialPingURI(that.loginTarget); + console.warn("Default ping target not available, will use loginTarget instead."); + } + setOepingAvailable(foundOeping); + + // If we're here, we've just logged in. If pingInterval has been set, we need + // to start autopinging + if (_pingInterval > 0) { + _timeoutID = setTimeout(that._autoping, _pingInterval); + } + } + }; + + /* + * args: pingURI + * async + * onCompleteFn used only if async is true + * + * (deliberately not catching thrown error) + */ + this._sendPing = function (args) { + var xhr = new XMLHttpRequest(), + that = this; + + function sendPingAfterOpen() { + if (args.async) { + xhr.onreadystatechange = args.onReadyStateFn; + xhr.onCompleteFn = args.onCompleteFn; + xhr._jsdosession = jsdosession; // in case the Session is part of a JSDOSession + xhr._deferred = args.deferred; // in case the Session is part of a JSDOSession + } + progress.data.Session._setNoCacheHeaders(xhr); + // set X-CLIENT-PROPS header + setRequestHeaderFromContextProps(that, xhr); + if (that.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { + _addWithCredentialsAndAccept(xhr, + "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + } + xhr.send(null); + } + + try { + if (this._authProvider) { + this._authProvider._openRequestAndAuthorize(xhr, + 'GET', + args.pingURI, + args.async, + sendPingAfterOpen); + } else { + // get rid of this if we do away with synchronous support (i.e., customer use of + // old Session API) + this._setXHRCredentials(xhr, "GET", args.pingURI, this.userName, _password, args.async); + + // Sending the XHR request after opening the channel + if (xhr.readyState === 1) { + sendPingAfterOpen(); + } + } + } catch (e) { + args.error = e; + } + + args.xhr = xhr; + }; + + this._makePingURI = function () { + var pingURI = this.serviceURI + partialPingURI; + // had caching problem with Firefox in its offline mode + if (progress.data.Session._useTimeStamp) { + pingURI = progress.data.Session._addTimeStampToURL(pingURI); + } + return pingURI; + }; + + + /* + * autoping -- callback + */ + this._autoping = function () { + that.ping({async: true}); + }; + + + // TODO for API revamp: get rid of this method and replace it with implementations + // of AUthenticationImplementation.openRequest that are specific to the + // auth models (assuming we can use some sort of subclassing or interface design) + // (and when we remove this, remove the calls to it in this file) + /* _setXHRCredentials (intended for progress.data library use only) + * set credentials as needed, both via the xhr's open method and setting the + * Authorization header directly + */ + this._setXHRCredentials = function (xhr, verb, uri, userName, password, async) { + + // note that we do not set credentials if userName is null. + // Null userName indicates that the developer is depending on the browser to + // get and manage the credentials, and we need to make sure we don't interfere with that + if (userName + && this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { + + // See the comment at the definition of the canPassCredentialsToOpen() function + // for why we pass credentials to open() in some cases but not others. (If we're not using + // Basic auth, we never pass credentials) + if (canPassCredentialsToOpen()) { + xhr.open(verb, uri, async, userName, password); + } + else { + xhr.open(verb, uri, async); + } + + // set Authorization header + var auth = _make_basic_auth(userName, password); + xhr.setRequestHeader('Authorization', auth); + } + else { + xhr.open(verb, uri, async); + } + }; + + /* _addCCIDtoURL (intended for progress.data library use only) + * Add the Client Context ID being used by a session on an OE REST application, if we have + * previously stored one from a response from the server + */ + this._addCCIDtoURL = function (url) { + var urlPart1, + urlPart2, + jsessionidStr, + index; + + if (this.clientContextId && (this.clientContextId !== "0")) { + // Should we test protocol, + // host and port in addition to path to ensure that jsessionid is only sent + // when request applies to the REST app (it might not be if the catalog is somewhere else) + if (url.substring(0, this.serviceURI.length) == this.serviceURI) { + jsessionidStr = ";" + "JSESSIONID=" + this.clientContextId; + index = url.indexOf('?'); + if (index == -1) { + url += jsessionidStr; // just append the jsessionid path parameter to the path + } + else { + // insert jsessionid path parameter before the first query parameter + urlPart1 = url.substring(0, index); + urlPart2 = url.substring(index); + url = urlPart1 + jsessionidStr + urlPart2; + } + } + } + return url; + }; + + /* _saveClientContextId (intended for progress.data library use only) + * If the CCID hasn't been set for the session yet, check the xhr for it and store it. + * (If it has been set, assume that the existing one is correct and do nothing. We could + * enhance this function by checking to see whether the new one matches the existing one. + * Not sure what to do if that's the case -- overwrite the old one? ignore the new one? + * Should at least log a warning or error + */ + this._saveClientContextId = function (xhr) { + // do this unconditionally (even if there is already a client-context-id), because + // if basic authentication is set up such that it uses sessions, and cookies are disabled, + // the server will generate a different session on each request and the X-CLIENT-CONTEXT-ID + // will therefore be different + setClientContextIDfromXHR(xhr, this); + }; + + this._parseCatalog = function (xhr) { + var jsonObject; + var catalogdata; + + try { + jsonObject = JSON.parse(xhr.responseText); + catalogdata = jsonObject.services; + } + catch (e) { + console.error("Unable to parse response. Make sure catalog has correct format."); + catalogdata = null; + } + + return catalogdata; + }; + + /* _prependAppURL + * Prepends the URL of the Web application + * (the 1st parameter passed to login, stored in this.serviceURI) + * to whatever string is passed in. If the string passed in is an absolute URL, this function does + * nothing except return a copy. This function ensures that the resulting URL has the correct number + * of slashes between the web app url and the string passed in (currently that means that if what's + * passed in has no initial slash, the function adds one) + */ + this._prependAppURL = function (oldURL) { + if (!oldURL) { + /* If oldURL is null, just return the app URL. (It's not the responsibility of this + * function to decide whether having a null URL is an error. Its only responsibility + * is to prepend the App URL to whatever it gets passed + * (and make sure the result is a valid URL) + */ + return this.serviceURI; + } + var newURL = oldURL; + var pat = /^https?:\/\//i; + if (!pat.test(newURL)) { + if (newURL.indexOf("/") !== 0) { + newURL = "/" + newURL; + } + + newURL = this.serviceURI + newURL; + } + return newURL; + }; + + + // Functions + + // get rid of this if we get rid of synchronous (old Session object API) support? + // Set an XMLHttpRequest object's withCredentials attribute and Accept header, + // using a try-catch so that if setting withCredentials throws an error it doesn't + // interrupt execution (this is a workaround for the fact that Firefox doesn't + // allow you to set withCredentials when you're doing a synchronous operation) + // The setting of the Accept header is included here, and happens after the + // attempt to set withCredentials, to make the behavior in 11.3.0 match + // the behavior in 11.2.1 -- for Firefox, in a CORS situation, login() will + // fail. (If we allowed the Accept header to be set, login() would succeed + // because of that but addCatalog() would fail because no JSESSIONID would + // be sent due to withCredentials not being true) + function _addWithCredentialsAndAccept(xhr, acceptString) { + try { + xhr.withCredentials = true; + xhr.setRequestHeader("Accept", acceptString); + } + catch (e) { + } + } + + // get rid of this if we get rid of synchronous (old Session API) support? + // (because it's in AuthenticationProviderBasic) + // from http://coderseye.com/2007/how-to-do-http-basic-auth-in-ajax.html + function _make_basic_auth(user, pw) { + var tok = user + ':' + pw; + var hash = btoa(tok); + return "Basic " + hash; + } + + /* The next 2 functions, _gotLoginForm() and _gotLoginFailure(), attempt to determine whether + * a server response consists of + * the application's login page or login failure page. Currently (release 11.2), this + * is the only way we have of determining that a request made to the server that's + * configured for form-based authentication failed due to authentication (i.e., + * authentication hadn't happened before the request and either invalid credentials or + * no credentials were sent to the server). That's because, due to the fact that the browser + * or native wrapper typically intercepts the redirect involved in an unauthenticated request + * to a server that's using using form auth, all we see in the XHR is a success status code + * plus whatever page we were redirected to. + * In the future, we expect to enhance the OE REST adapter so that it will return a status code + * indicating failure for form-based authentication, and we can reimplement these functions so + * they check for that code rather than do the simplistic string search. + */ + + // Determines whether the content of the xhr is the login page. Assumes + // use of a convention for testing for login page + var loginFormIDString = "j_spring_security_check"; + + function _gotLoginForm(xhr) { + // is the response contained in an xhr actually the login page? + return _findStringInResponseHTML(xhr, loginFormIDString); + } + + // Determines whether the content of the xhr is the login failure page. Assumes + // use of a convention for testing for login fail page + var loginFailureIdentificationString = "login failed"; + + function _gotLoginFailure(xhr) { + return _findStringInResponseHTML(xhr, loginFailureIdentificationString); + } + + // Does a given xhr contain html and does that html contain a given string? + function _findStringInResponseHTML(xhr, searchString) { + if (!xhr.responseText) { + return false; + } + var contentType = xhr.getResponseHeader("Content-Type"); + + if ((contentType.indexOf("text/html") >= 0) && + (xhr.responseText.indexOf(searchString) >= 0)) { + return true; + } + + return false; + } + + // get rid of this if we get rid of synchronous (old Session API) support? + /* sets the statusFromjson property in the params object to indicate + * the status of a response from an OE Mobile Web application that has + * to do with authentication (the response to a login request, or a + * response to a request for a resource where there was an error having + * to do with authentication */ + function handleJSONLoginResponse(params) { + // Parse the json in the response to see whether it's the special OE REST service + // response. If it is, check the result (which should be consistent with the status from + // the xhr) + var jsonObject; + params.statusFromjson = null; + try { + jsonObject = JSON.parse(params.xhr.responseText); + + if (jsonObject.status_code !== undefined + && jsonObject.status_txt !== undefined) { + params.statusFromjson = jsonObject.status_code; + } + } + catch (e) { + // invalid json + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, params.session); + setLoginHttpStatus(xhr.status, params.session); + throw new Error("Unable to parse login response from server."); + } + + } + + function setRequestHeaderFromContextProps(session, xhr) { + if (session.xClientProps) { + xhr.setRequestHeader("X-CLIENT-PROPS", session.xClientProps); + } + else if (session._contextProperties.contextHeader !== undefined) { + xhr.setRequestHeader("X-CLIENT-PROPS", session._contextProperties.contextHeader); + } + } + + function toggleOnlineState(xhr) { + var pdsession = that; + + setLoginHttpStatus(xhr.status, pdsession); + + if (pdsession.loginHttpStatus >= 200 && pdsession.loginHttpStatus < 400) { + setLoginResult(progress.data.Session.LOGIN_SUCCESS, pdsession); + setRestApplicationIsOnline(true); + pdsession._saveClientContextId(xhr); + storeAllSessionInfo(); // save info to persistent storage + } else { + // Taking a page from _processPingResult where we set the rest application as offline if it's one of + // these error codes + if (pdsession.loginHttpStatus === 0 || pdsession.loginHttpStatus === 400 || pdsession.loginHttpStatus === 410) { + setRestApplicationIsOnline(false); + setLoginResult(progress.data.AuthenticationProvider._getAuthFailureReason(xhr), + pdsession); + } + // Otherwise if it's probably an internal error or auth problem. Either way, we know it's still online. + else { + + setRestApplicationIsOnline(true); + setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, pdsession); + } + + + } + + setLastSessionXHR(xhr, pdsession); + updateContextPropsFromResponse(pdsession, xhr); + + return pdsession.loginResult; + }; + + function updateContextPropsFromResponse(session, xhr) { + /* determine whether the response contains an X-CLIENT_PROPS header and, if so, + set the Session's context + */ + var contextString, + context; + + if (xhr) { + contextString = getResponseHeaderNoError(xhr, "X-CLIENT-PROPS"); + if (contextString) { + try { + context = JSON.parse( contextString ); + } + catch(e) { + } + if (typeof context === "object") { + session._contextProperties.setContext( context ); + } + else { + //{1}: A server response included an invalid {2} header. + throw new Error(progress.data._getMsgText("jsdoMSG123", 'Session', 'X-CLIENT-PROPS')); + } + } + else if (contextString === "") { + // If header is "", clear the X-CLIENT-PROPS context, + session._contextProperties.setContext( {} ); + } + // if header is absent (getResponseHeader will return null), don't change _contextProperties + } + } + + // Remove all resources, services, and sessions related to this Session from the ServicesManager + function cleanServicesManager() { + progress.data.ServicesManager.cleanSession(that); + } + + // process constructor options and do other initialization + + // If a storage key (name property of a JSDOSession) was passed to the constructor, + // use it to try to retrieve state data from a previous JSDOSession instance that + // had the same name. This code was introduced to handle page refreshes, but could + // be used for other purposes. + if (typeof (options) === 'object') { + + jsdosession = options.jsdosession; + newURI = options.serviceURI; + setAuthProvider(options.authProvider); // do this BEFORE calling setSessionInfoFromStorage + + if (options.authProvider && options.authProvider.hasClientCredentials()) { + _loginResult = progress.data.Session.LOGIN_SUCCESS; + } + + // get rid of trailing '/' because appending service url that starts with '/' + // will cause request failures + if (newURI && newURI[newURI.length - 1] === "/") { + newURI = newURI.substring(0, newURI.length - 1); + } + + _storageKey = options._storageKey; + if (_storageKey) { + if (retrieveSessionInfo(_storageKey)) { + storedAuthModel = retrieveSessionInfo("authenticationModel"); + storedURI = retrieveSessionInfo("serviceURI"); + + if ((storedAuthModel !== options.authenticationModel) || + (storedURI !== newURI)) { + clearAllSessionInfo(); + } else { + // Note: be sure we have set authProvider (if any) from options before + // calling setSessionInfoFromStorage (important so that the logic in + // setSessionInfoFromStorage that re-creates an AuthenticationProvider + // after page refresh only gets used if the app is using the old JSDOSession.login) + setSessionInfoFromStorage(_storageKey); + stateWasReadFromStorage = true; + } + } + // _storageKey is in essence the flag for page refresh; we are not supporting page refresh for Basic + // auth, so clear it even if it was passed in. + // (But had to set and keep _storageKey until this point so that the above validation of + // serviceURI and auth model will be done even in the case where there's a mismatch and + // the new auth model is Basic. This statement will go away when we support page refresh with + // Basic) + if (options.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { + _storageKey = undefined; + } + } + + // If we didn't read state info from storage, we need to set the serviceURI and probably + // the authenticationModel + if (!stateWasReadFromStorage) { + if (newURI) { + setServiceURI(newURI, this); + } + if (options.authenticationModel) { + this.authenticationModel = options.authenticationModel; + } + } + } + + }; // End of Session + progress.data.Session._useTimeStamp = true; + + var SEQ_MAX_VALUE = 999999999999999; + // 15 - 9 + var _tsseq = SEQ_MAX_VALUE; + // Initialized to SEQ_MAX_VALUE to initialize values. + var _tsprefix1 = 0; + var _tsprefix2 = 0; + + // this._getNextTimeStamp = function () { + progress.data.Session._getNextTimeStamp = function () { + var seq = ++_tsseq; + if (seq >= SEQ_MAX_VALUE) { + _tsseq = seq = 1; + var t = Math.floor(( Date.now ? Date.now() : (new Date().getTime())) / 10000); + if (_tsprefix1 == t) { + _tsprefix2++; + if (_tsprefix2 >= SEQ_MAX_VALUE) { + _tsprefix2 = 1; + } + } + else { + _tsprefix1 = t; + Math.random(); // Ignore call to random + _tsprefix2 = Math.round(Math.random() * 10000000000); + } + } + + return _tsprefix1 + "-" + _tsprefix2 + "-" + seq; + }; + + /* + * _addTimeStampToURL (intended for progress.data library use only) + * Add a time stamp to the a URL to prevent caching of the request. + * Set progress.data.Session._useTimeStamp = false to turn off. + */ + progress.data.Session._addTimeStampToURL = function (url) { + var timeStamp = "_ts=" + progress.data.Session._getNextTimeStamp(); + url += ((url.indexOf('?') == -1) ? "?" : "&") + timeStamp; + return url; + }; + + // Do whatever it takes to direct the XMLHttpRequest not to fulfill the request + // from a cache + // (convenience method --- we do this several different places in the code) + progress.data.Session._setNoCacheHeaders = function (xhr) { + xhr.setRequestHeader("Cache-Control", "no-cache"); + xhr.setRequestHeader("Pragma", "no-cache"); + }; + + + +// Constants for progress.data.Session + if ((typeof Object.defineProperty) == 'function') { + Object.defineProperty(progress.data.Session, 'LOGIN_AUTHENTICATION_REQUIRED', { + value: 0, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'LOGIN_SUCCESS', { + value: 1, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'LOGIN_AUTHENTICATION_FAILURE', { + value: 2, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'LOGIN_GENERAL_FAILURE', { + value: 3, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'CATALOG_ALREADY_LOADED', { + value: 4, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'ASYNC_PENDING', { + value: 5, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'EXPIRED_TOKEN', { + value: 6, enumerable: true + }); + + Object.defineProperty(progress.data.Session, 'SUCCESS', { + value: 1, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'AUTHENTICATION_FAILURE', { + value: 2, enumerable: true + }); + Object.defineProperty(progress.data.Session, 'GENERAL_FAILURE', { + value: 3, enumerable: true + }); + + Object.defineProperty(progress.data.Session, 'AUTH_TYPE_ANON', { + value: "anonymous", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'AUTH_TYPE_BASIC', { + value: "basic", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'AUTH_TYPE_FORM', { + value: "form", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'AUTH_TYPE_SSO', { + value: "sso", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'AUTH_TYPE_FORM_SSO', { + value: "form_sso", enumerable: true + }); + + + Object.defineProperty(progress.data.Session, 'DEVICE_OFFLINE', { + value: "Device is offline", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'SERVER_OFFLINE', { + value: "Cannot contact server", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'WEB_APPLICATION_OFFLINE', { + value: "Mobile Web Application is not available", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'SERVICE_OFFLINE', { + value: "REST web Service is not available", enumerable: true + }); + Object.defineProperty(progress.data.Session, 'APPSERVER_OFFLINE', { + value: "AppServer is not available", enumerable: true + }); + } + else { + progress.data.Session.LOGIN_SUCCESS = 1; + progress.data.Session.LOGIN_AUTHENTICATION_FAILURE = 2; + progress.data.Session.LOGIN_GENERAL_FAILURE = 3; + progress.data.Session.CATALOG_ALREADY_LOADED = 4; + + progress.data.Session.SUCCESS = 1; + progress.data.Session.AUTHENTICATION_FAILURE = 2; + progress.data.Session.GENERAL_FAILURE = 3; + + progress.data.Session.AUTH_TYPE_ANON = "anonymous"; + progress.data.Session.AUTH_TYPE_BASIC = "basic"; + progress.data.Session.AUTH_TYPE_FORM = "form"; + progress.data.Session.AUTH_TYPE_SSO = "sso"; + + /* deliberately not including the "offline reasons" that are defined in the + * 1st part of the conditional. We believe that we can be used only in environments where + * ECMAScript 5 is supported, so let's put that assumption to the test + */ + } + +//setup inheritance for Session -- specifically for incorporating an Observable object + progress.data.Session.prototype = new progress.util.Observable(); + progress.data.Session.prototype.constructor = progress.data.Session; + function validateSessionSubscribe(args, evt, listenerData) { + listenerData.operation = undefined; + var found = false; + + // make sure this event is one that we support + for (var i = 0; i < this._eventNames.length; i++) { + if (evt === this._eventNames[i].toLowerCase()) { + found = true; + break; + } + } + if (!found) { + throw new Error(progress.data._getMsgText("jsdoMSG042", evt)); + } + + if (args.length < 2) { + throw new Error(progress.data._getMsgText("jsdoMSG038", 2)); + } + + if (typeof args[0] !== 'string') { + throw new Error(progress.data._getMsgText("jsdoMSG039")); + } + + if (typeof args[1] !== 'function') { + throw new Error(progress.data._getMsgText("jsdoMSG040")); + } + else { + listenerData.fn = args[1]; + } + + if (args.length > 2) { + if (typeof args[2] !== 'object') { + throw new Error(progress.data._getMsgText("jsdoMSG041", evt)); + } + else { + listenerData.scope = args[2]; + } + } + } + // events supported by Session + progress.data.Session.prototype._eventNames = + ["offline", "online", "afterLogin", "afterAddCatalog", "afterLogout", "afterDisconnect"]; + // callback to validate subscribe and unsubscribe + progress.data.Session.prototype.validateSubscribe = validateSessionSubscribe; + progress.data.Session.prototype.toString = function (radix) { + return "progress.data.Session"; + }; + + + /* + progress.data.JSDOSession + Like progress.data.Session, but the methods are async-only and return promises. + (first implementation uses progress.data.Session to do the work, but conceivably + that implementation could be changed to something different) + The JSDOSession object keeps the same underlying pdsession object for the lifetime + of the JSDOSession object -- i.e., even after logout and subsequent login, the pdsession + is re-used rather than re-created. + */ + progress.data.JSDOSession = function JSDOSession(options) { + var _pdsession, + _serviceURI, + that = this, + _name; + + // PROPERTIES + // Approach: Use the properties of the underlying progress.data.Session object whenever + // possible. + Object.defineProperty(this, 'authenticationModel', + { + get: function () { + return _pdsession ? _pdsession.authenticationModel : undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'authProvider', + { + get: function () { + return _pdsession ? _pdsession._authProvider : null; + }, + enumerable: true + }); + Object.defineProperty(this, 'catalogURIs', + { + get: function () { + return _pdsession ? _pdsession.catalogURIs: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'clientContextId', + { + get: function () { + return _pdsession ? _pdsession.clientContextId: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'connected', + { + get: function () { + return _pdsession ? _pdsession.connected: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'JSDOs', + { + get: function () { + return _pdsession ? _pdsession.JSDOs: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'loginResult', + { + get: function () { + return _pdsession ? _pdsession.loginResult: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'loginHttpStatus', + { + get: function () { + return _pdsession ? _pdsession.loginHttpStatus: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'onOpenRequest', + { + get: function () { + return _pdsession ? _pdsession.onOpenRequest: undefined; + }, + set: function (newval) { + if (_pdsession) { + _pdsession.onOpenRequest = newval; + } + }, + enumerable: true + }); + + Object.defineProperty(this, 'pingInterval', + { + get: function () { + return _pdsession ? _pdsession.pingInterval: undefined; + }, + set: function (newval) { + if (_pdsession) { + _pdsession.pingInterval = newval; + } + }, + enumerable: true + }); + + Object.defineProperty(this, 'services', + { + get: function () { + return _pdsession ? _pdsession.services: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'serviceURI', + { + get: function () { + if (_pdsession && _pdsession.serviceURI) { + return _pdsession.serviceURI; + } + else { + return _serviceURI; + } + }, + enumerable: true + }); + + Object.defineProperty(this, 'userName', + { + get: function () { + return _pdsession ? _pdsession.userName: undefined; + }, + enumerable: true + }); + + Object.defineProperty(this, 'name', + { + get: function () { + return _name; + }, + enumerable: true + }); + + Object.defineProperty( + this, + "_isInvalidated", + { + get: function () { + return _pdsession._isInvalidated; + }, + enumerable: false + } + ); + + // PRIVATE FUNCTIONS + + + // Wrapper to make it easier to change the promise implementation we use. + // Note that in the JSDO library's first implementation of promise support, + // the "promise" parameter for this function is actually a jQuery Deferred object + function settlePromise(promise, fulfill, result, info) { + if (fulfill) { + promise.resolve(that, result, info); + } else { + promise.reject(that, result, info); + } + } + + // use this for the events fired by progress.data.Session that can be handled with common code + function genericSessionEventHandler(pdsession, result, errorObject, xhr, deferred) { + var myDeferred; + + if (xhr) { + myDeferred = xhr._deferred; + } else { + myDeferred = deferred; + } + + settlePromise(myDeferred, + result === progress.data.Session.SUCCESS ? true : false, + result, + { errorObject: errorObject, + xhr: xhr }); + } + + function onAfterAddCatalog( pdsession, result, errorObject, xhr ) { + var deferred, + fulfill = false, + settleResult; + + if (result === progress.data.Session.EXPIRED_TOKEN) { + settleResult = progress.data.Session.EXPIRED_TOKEN; + } else { + settleResult = progress.data.Session.GENERAL_FAILURE; + } + + if (xhr && xhr._deferred) { + deferred = xhr._deferred; + + /* add the result for this addCatalog to the result array. */ + if ( result !== progress.data.Session.SUCCESS && + result !== progress.data.Session.CATALOG_ALREADY_LOADED ) { + + result = result || progress.data.Session.GENERAL_FAILURE; + + /* Set a property on the deferred to indicates that the "overall" result was + a failure. When we decide whether to reject or resolve the promise, we reject + if it's set to GENERAL_FAILURE, otherwise we resolve the promise + (really only need to set this once, but simpler code if we just set (or possibly + re-set) it whenever we find an error, plus if, at some point while we're still + processing, it's important to know whether we've already had an error, we can + check the property) + */ + deferred._overallCatalogResult = progress.data.Session.GENERAL_FAILURE; + } + + deferred._results[xhr._catalogIndex] = { catalogURI : xhr._catalogURI, + result : result, + errorObject : errorObject, + xhr : xhr}; + deferred._numCatalogsProcessed += 1; + if ( deferred._numCatalogsProcessed === deferred._numCatalogs ) { + deferred._processedPromise = true; + + if ( !deferred._overallCatalogResult ) { + fulfill = true; + settleResult = progress.data.Session.SUCCESS; + } + settlePromise(xhr._deferred, + fulfill, + settleResult, + xhr._deferred._results); + } + } + } + + function onAfterLogout(pdsession, errorObject, xhr) { + var result = progress.data.Session.GENERAL_FAILURE, + fulfill = false; + if (xhr && xhr._deferred) { + /* Note: loginResult gets cleared on successful logout, so testing it for false + to confirm that logout succeeded + */ + if (!errorObject && !pdsession.loginResult) { + result = progress.data.Session.SUCCESS; + fulfill = true; + } + settlePromise(xhr._deferred, + fulfill, + result, + { errorObject: errorObject, + xhr: xhr }); + } + } + + function onPingComplete(args) { + var xhr = args.xhr; + if (xhr && xhr._deferred) { + settlePromise(xhr._deferred, + args.pingResult, // this tells settlePromise whether to resolve or reject + args.pingResult, // this is the result value passed to the promise handler + { offlineReason: args.offlineReason, + xhr: xhr }); + } + } + + // METHODS + + // login() + // Creates an AuthenticationProvider and calls its login() method. Any errors thrown by the + // Auth Provider's constructor or login will bubble up to the caller, otherwise this method + // returns the promise from the A-P's login call. + this.login = function (username, password, options) { + var deferred = $.Deferred(), + iOSBasicAuthTimeout; + + function callIsAuthorized() { + that.isAuthorized() + .then(function (jsdosession, result, info) { + deferred.resolve(that, result, info); + }, function (jsdosession, result, info) { + deferred.reject(that, result, info); + }); + } + + if (this._isInvalidated) { + // JSDOSession: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); + } + + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // JSDOSession: Cannot call login() when authenticationModel is SSO. + // Please use the AuthenticationProvider object instead. + throw new Error(progress.data._getMsgText("jsdoMSG057", + 'JSDOSession', + 'login()')); + } + + if (typeof options === 'object') { + iOSBasicAuthTimeout = options.iOSBasicAuthTimeout; + } + + if (!_pdsession._authProvider) { + // is there a better way to do this? Need it because we didn't have the authprovider when + // running the constructor + _pdsession._authProvider = new progress.data.AuthenticationProvider({ + uri: this.serviceURI, + authenticationModel: this.authenticationModel + }); + } + + + _pdsession._authProvider.logout() + .then( function () { + return _pdsession._authProvider.login(username, password); + }) + .then(function () { + callIsAuthorized(); + }, function (provider, result, info) { + deferred.reject(that, result, info); + }); + + return deferred.promise(); + }; + + // This method terminates the JSDOSession's ability to send requests to its serviceURI. + // Remove the reference to the AuthenticationProvider that was passed to connect(). + // Will be a no-op if connect() has not yet been called successfully. + // This method reinitializes the Session object back to the state it was in just after being created. + // Retains the serviceURI, authenticationModel, and name values. + // Delete any of the object's data that had been persisted (for example, to sessionStorage to support + // page refresh). + // Data for any catalogs loaded by the JSDOSession will NOT be deleted. + // See additional commecnts at the Session._disconnect method. + this.disconnect = function () { + var deferred = $.Deferred(), + errorObject; + + try { + _pdsession.subscribe('afterDisconnect', genericSessionEventHandler, this); + + _pdsession._disconnect(deferred); + } catch (e) { + // JSDOSession: Unexpected error calling disconnect: {e.message} + errorObject = new Error(progress.data._getMsgText("jsdoMSG049", "JSDOSession", "disconnect", e.message)); + } + + if (errorObject) { + throw errorObject; + } else { + return deferred.promise(); + } + }; + + this.addCatalog = function (catalogURI, unameOrOpts, password, opts) { + var deferred = $.Deferred(), + catalogURIs, + numCatalogs, + catalogIndex, + addResult, + errorObject, + iOSBasicAuthTimeout, + username, + options, + authProvider; + + if (this._isInvalidated) { + // JSDOSession: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); + } + + // check whether 1st param is a string or an array + if (typeof catalogURI === "string") { + catalogURIs = [catalogURI]; + } else if (catalogURI instanceof Array) { + catalogURIs = catalogURI; + } else { + throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "addCatalog", + "The first argument must be a string or an array of strings specifying the URI of the catalog.")); + } + + // type check the 2nd param if it exists + if (unameOrOpts) { + if (typeof unameOrOpts === "string") { + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // Session: Cannot pass username and password to addCatalog when + // authenticationModel is SSO. Pass an AuthenticationProvider instead. + throw new Error(progress.data._getMsgText("jsdoMSG058", 'Session')); + } + username = unameOrOpts; + // explictly ignore any authProvider if using the (catURI, uname, pw, options) signature + if (opts) { + options = opts; + options.authProvider = undefined; + } + } else if (typeof unameOrOpts === "object") { + options = unameOrOpts; + } else { + // JSDOSession: Argument 2 must be of type object in addCatalog call. + throw new Error(progress.data._getMsgText("jsdoMSG121", "JSDOSession", "2", + "object", "addCatalog")); + } + } + + if (typeof options === 'object') { + // possible override for the workaround for the Cordova iOS async Basic auth bug + iOSBasicAuthTimeout = options.iOSBasicAuthTimeout; + if (options.authProvider) { + authProvider = options.authProvider; + } else if (this.authProvider) { + authProvider = this.authProvider; + } + } + + // Error out if no authProvider or username was given + if (!authProvider && !this.authProvider && !username) { + throw new Error(progress.data._getMsgText("jsdoMSG511")); + } + + /* When we're done processing all catalogs, we pass an array of results to resolve() or + reject(). We're attaching this array to the deferred object, in case the app makes + multiple addCatalog calls (if the array was attached to the JSDOSession, + the 2nd call might overwrite the first) + */ + + /* Add properties to the deferred object for this call to store the total + number of catalogs that are to be done, the number that ahve been processed, + and a reference to an array of results. + Loop through the array of catalogURIs, calling addCatalog for each one. If a call + throws an error or returns something other than ASYNC_PENDING, create a result object + for that catalog and add the result object to the resultArray. Otherwise, the result + object will be added by the afterAddCatalog handler. + If all of the Session.addCatalog calls throw an error or return something other + than ASYNC_PENDING, this function will reject the promise and return. Otherwise + the afterAddCatalog handler will resolve or reject the promise after all calls have + been processed. + Note that we try to make sure that each entry in the results array is in the same position + as its catalogURI in the input array. + */ + // if a catalogURI has no protocol, pdsession will assume it's relative to the serviceURI, + // if there has been a login + // NOTE: this means if the app is trying to load a local catalog, it MUST + // specify the file: protocol (and we need to make sure that works on all platforms) + + _pdsession.subscribe('afterAddCatalog', onAfterAddCatalog, this); + + numCatalogs = catalogURIs.length; + deferred._numCatalogs = numCatalogs; + deferred._numCatalogsProcessed = 0; + deferred._results = []; + deferred._results.length = numCatalogs; + + for ( catalogIndex = 0; catalogIndex < numCatalogs; catalogIndex += 1) { + errorObject = undefined; + addResult = undefined; + try { + addResult = _pdsession.addCatalog( + { catalogURI : catalogURIs[catalogIndex], + async : true, + userName : username, + password : password, + deferred : deferred, + catalogIndex : catalogIndex, + iOSBasicAuthTimeout : iOSBasicAuthTimeout, + authProvider : authProvider, + offlineAddCatalog : true } ); // OK to get catalog if offline + } + catch (e) { + errorObject = new Error("JSDOSession: Unable to send addCatalog request. " + e.message); + } + + if ( addResult !== progress.data.Session.ASYNC_PENDING ) { + /* Set a property on the deferred to indicate that the "overall" result was + a failure. When we decide whether to reject or resolve the promise, we reject + if it's set to GENERAL_FAILURE, otherwise we resolve the promise + (really only need to set this once, but simpler code if we just set (or possibly + re-set) it whenever we find an error, plus if, at some point while we're still + processing, it's important to know whether we've already had an error, we can + check the property) + */ + deferred._overallCatalogResult = progress.data.Session.GENERAL_FAILURE; + if ( errorObject ) { + addResult = progress.data.Session.GENERAL_FAILURE; + } + deferred._results[catalogIndex] = { catalogURI : catalogURIs[catalogIndex], + result : addResult, + errorObject : errorObject, + xhr : undefined }; + deferred._numCatalogsProcessed += 1; + } + } + + if ( (deferred._numCatalogsProcessed === numCatalogs) && !deferred._processedPromise ) { + /* The goal here is to handle the case where all the catalogs + have been processed but the afterAddCatalog handler may not be invoked at the + end (the obvious example is if there are no async requests actually made by + Session.addCatalog). In that case, we have to resolve/reject from here. Chances are + very good that if we're doing this here, there's been at least one error, but just + to be sure, we check the deferred._overallCatalogResult anyway + */ + if ( deferred._overallCatalogResult === progress.data.Session.GENERAL_FAILURE ) { + deferred.reject( this, progress.data.Session.GENERAL_FAILURE, deferred._results ); + } + else { + deferred.resolve( this, progress.data.Session.SUCCESS, deferred._results ); + } + } + + return deferred.promise(); + }; + + // Note that this will work for either of these cases: + // - app originally called JSDOSession.login (so we implicitly created the AuthenticationProvider) + // - app created an AuthenticationProvider and passed it to connect, but now for some reason has + // called logout (this is actually a nice shortcut for someone who has used getSession) + // (NB: we should not allow this for SSO, tho) + // + // Note that we also don't support login/logout on the JSDOSession for page refresh + this.logout = function(){ + var deferred = $.Deferred(), + authProv = this.authProvider; + + if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // JSDOSession: Cannot call logout() when authenticationModel is SSO. + // Please use the AuthenticationProvider object instead. + throw new Error(progress.data._getMsgText("jsdoMSG057", + 'JSDOSession', + 'logout()')); + } + + this.disconnect() + .then(function () { + if (authProv) { + return authProv.logout(); + } + // if there's no AP, just resolve immediately + deferred.resolve(that, progress.data.Session.SUCCESS, {}); + }) + .then(function (jsdosession, result, info) { + deferred.resolve(that, result, info); + }, + // catches errors on either login or connect + function (provider, result, info) { + deferred.reject(that, result, info); + } + ); + + return deferred.promise(); + }; + + this.invalidate = function () { + _pdsession.invalidate(); + return this.logout(); + }; + + this.ping = function() { + var deferred = $.Deferred(); + + if (this._isInvalidated) { + // JSDOSession: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); + } + + try { + _pdsession.ping( {async: true, + deferred : deferred, + onCompleteFn : onPingComplete } ); + } + catch(e) { + throw new Error("JSDOSession: Unable to send ping request. " + e.message); + } + + return deferred.promise(); + }; + + // Determine whether the JSDOSession can currently access its web application. + // The use expected for this method is to determine whether a JSDOSession that has + // previously authenticated to its web application still has authorization. + // For example, if the JSDOSession is using Form authentication, is the server + // session still valid or did it expire? + this.isAuthorized = function () { + var deferred = $.Deferred(), + xhr = new XMLHttpRequest(), + result, + that = this; + + if (this._isInvalidated) { + // JSDOSession: This session has been invalidated and cannot be used. + throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); + } + + // If we logged in successfuly using login() or if we have an AuthProvider, make the call + if (this.loginResult === progress.data.Session.LOGIN_SUCCESS || this.authProvider) { + _pdsession._openRequest(xhr, "GET", _pdsession.loginTarget, true, + function () { + xhr.onreadystatechange = function () { + // do we need this xhr var? The one declared in isAuthorized seems to be in scope + var xhr = this, + cbresult, + fakePingArgs, + info; + + if (xhr.readyState === 4) { + info = {xhr: xhr, + offlineReason: undefined, + fireEventIfOfflineChange: true, + usingOepingFormat: false + }; + + // call _processPingResult because it has logic for + // detecting change in online/offline state + _pdsession._processPingResult(info); + + if (xhr.status >= 200 && xhr.status < 300) { + deferred.resolve(that, + progress.data.Session.SUCCESS, + info); + } else { + if (xhr.status === 401) { + cbresult = progress.data.AuthenticationProvider._getAuthFailureReason(xhr); + } else { + cbresult = progress.data.Session.GENERAL_FAILURE; + } + deferred.reject(that, cbresult, info); + } + } + }; + + try { + xhr.send(); + } catch (e) { + throw new Error("JSDOSession: Unable to validate authorization. " + e.message); + } + } + ); + } else { + // Never logged in (or logged in and logged out). Regardless of what the reason + // was that there wasn't a login, the bottom line is that authentication is required + result = progress.data.Session.LOGIN_AUTHENTICATION_REQUIRED; + deferred.reject(that, result, {xhr: xhr}); + } + + return deferred.promise(); + }; + + /* + set the properties that are passed between client and Web application in the + X-CLIENT-PROPS header. This sets the complete set of properties all at once; + it replaces any existing context + */ + this.setContext = function( context ) { + _pdsession._contextProperties.setContext( context ); + }; + + /* + * Set or remove an individual property in the set of the properties that are passed + * between client and Web application in the X-CLIENT-PROPS header. This operates only + * on the property identiofied by propertyName; all other existing properties remain + * as they are. + * If the propertyName is not part of the context, this call adds it + * If it is part of the context, this call updates it, unless - + * If propertyValue is undefined, this call removes the property + */ + this.setContextProperty = function( propertyName, propertyValue) { + _pdsession._contextProperties.setContextProperty( propertyName, propertyValue ); + }; + + /* + * get the set of properties that are passed between client and Web application in the + * X-CLIENT-PROPS header. Returns an object that has the properties + */ + this.getContext = function( ) { + return _pdsession._contextProperties.getContext(); + }; + + /* get the value of an individual property that is in the set of properties passed between + * client and Web application in the X-CLIENT-PROPS header + */ + this.getContextProperty = function( propertyName) { + return _pdsession._contextProperties.getContextProperty( propertyName ); + }; + + + this._onlineHandler = function( session, request ) { + that.trigger( "online", that, request ); + }; + + this._offlineHandler = function( session, offlineReason, request ) { + that.trigger( "offline", that, offlineReason, request ); + }; + + // PROCESS CONSTRUCTOR ARGUMENTS + // validate constructor input arguments + if ( (arguments.length > 0) && (typeof(arguments[0]) === 'object') ) { + + // (options is the name of the arguments[0] parameter) + if (options.serviceURI && (typeof(options.serviceURI) === "string" ) ) { + _serviceURI = options.serviceURI; + } + else { + throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", + "The options parameter must include a 'serviceURI' property that is a string.") ); + } + + if (options.authenticationModel) { + if (typeof(options.authenticationModel) !== "string" ) { + throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", + "The authenticationModel property of the options parameter must be a string.") ); + } + + options.authenticationModel = options.authenticationModel.toLowerCase(); + } else { + options.authenticationModel = progress.data.Session.AUTH_TYPE_ANON; + } + + // TODO: clean this up. Maybe make an immediate function + if (options.authProvider) { + if (typeof options.authProvider !== 'object') { + // JSDOSession: The 'options' parameter passed to the 'constructor' function + // has an invalid value for the 'authProvider' property. + throw new Error(progress.data._getMsgText( + "jsdoMSG502", + "JSDOSession", + "options", + "constructor", + "authProvider" + )); + } + + if ((options.authProvider.authenticationModel !== progress.data.Session.AUTH_TYPE_FORM_SSO + && options.authProvider.authenticationModel !== options.authenticationModel) || + (options.authProvider.authenticationModel === progress.data.Session.AUTH_TYPE_FORM_SSO + && options.authenticationModel !== progress.data.Session.AUTH_TYPE_SSO)) { + // JSDOSession: Error in constructor. The authenticationModels of the " + + // AuthenticationProvider ({2}) and the JSDOSession ({3}) were not compatible."; + throw new Error(progress.data._getMsgText("jsdoMSG059", "JSDOSession", + options.authProvider.authenticationModel, options.authenticationModel)); + } + // Check if the provider exposes the required API. + if (typeof options.authProvider.hasClientCredentials === 'function') { + if (!options.authProvider.hasClientCredentials()) { + // JSDOSession: The AuthenticationProvider is not managing valid credentials. + throw new Error(progress.data._getMsgText("jsdoMSG125", "JSDOSession")); + } + } else { + // JSDOSession: AuthenticationProvider objects must have a hasClientCredentials method. + throw new Error(progress.data._getMsgText("jsdoMSG505", + "JSDOSession", + "AuthenticationProvider", + "hasClientCredentials")); + } + } else if (options.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + // JSDOSession: If a JSDOSession object is using the SSO authentication model, + // the options object passed to its constructor must include an authProvider property. + throw new Error(progress.data._getMsgText("jsdoMSG508")); + } + + } + else { + throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", + "The options argument was missing or invalid.") ); + } + + _name = options.name; + + _pdsession = new progress.data.Session({_storageKey: _name, + _silent: true, + authenticationModel: options.authenticationModel, + serviceURI: options.serviceURI, + jsdosession: this, + authProvider: options.authProvider}); + + try { + if (options.context) { + this.setContext(options.context); + } + _pdsession.subscribe( "online", this._onlineHandler, this); + _pdsession.subscribe( "offline", this._offlineHandler, this); + } catch (err) { + _pdsession = undefined; // so it will be garbage collected + throw err; + } + + }; // end of JSDOSession + +//set up inheritance for JSDOSession -- specifically for incorporating an Observable object + progress.data.JSDOSession.prototype = new progress.util.Observable(); + progress.data.JSDOSession.prototype.constructor = progress.data.JSDOSession; + function validateJSDOSessionSubscribe(args, evt, listenerData) { + listenerData.operation = undefined; + var found = false; + + // make sure this event is one that we support + for (var i = 0; i < this._eventNames.length; i++) { + if (evt === this._eventNames[i].toLowerCase()) { + found = true; + break; + } + } + if (!found) { + throw new Error(progress.data._getMsgText("jsdoMSG042", evt)); + } + + if (args.length < 2) { + throw new Error(progress.data._getMsgText("jsdoMSG038", 2)); + } + + if (typeof args[0] !== 'string') { + throw new Error(progress.data._getMsgText("jsdoMSG039")); + } + + if (typeof args[1] !== 'function') { + throw new Error(progress.data._getMsgText("jsdoMSG040")); + } + else { + listenerData.fn = args[1]; + } + + if (args.length > 2) { + if (typeof args[2] !== 'object') { + throw new Error(progress.data._getMsgText("jsdoMSG041", evt)); + } + else { + listenerData.scope = args[2]; + } + } + } + // events supported by JSDOSession + progress.data.JSDOSession.prototype._eventNames = + ["offline", "online"]; + // callback to validate subscribe and unsubscribe + progress.data.JSDOSession.prototype.validateSubscribe = validateJSDOSessionSubscribe; + progress.data.JSDOSession.prototype.toString = function (radix) { + return "progress.data.JSDOSession"; + }; + + progress.data.getSession = function (options) { + var deferred = $.Deferred(), + authProvider, + promise, + authProviderInitObject = {}; + + // This is the reject handler for session-related operations + // login, addCatalog, and logout + function sessionRejectHandler(originator, result, info) { + // undo the AuthenticationProvider's login if it succeeded + if (authProvider && authProvider.hasClientCredentials()) { + authProvider.logout() + .always(function () { + deferred.reject(result, info); + }); + } else { + deferred.reject(result, info); + } + } + + // This is the reject handler for the login callback + function callbackRejectHandler(reason) { + deferred.reject(progress.data.Session.GENERAL_FAILURE, {"reason": reason}); + } + + function loginHandler(provider) { + var jsdosession; + + try { + jsdosession = new progress.data.JSDOSession(options); + try { + jsdosession.isAuthorized() + .then(function() { + return jsdosession.addCatalog(options.catalogURI); + }, sessionRejectHandler) + .then(function (jsdosession, result, info) { + deferred.resolve(jsdosession, progress.data.Session.SUCCESS); + }, sessionRejectHandler); + } catch (e) { + sessionRejectHandler(jsdosession, + progress.data.Session.GENERAL_FAILURE, + {errorObject: e}); + } + } catch (e) { + sessionRejectHandler(jsdosession, + progress.data.Session.GENERAL_FAILURE, + {errorObject: e}); + } + } + + // This function calls login using credentials from the appropriate source + // Note that as currently implemented, this should NOT be called when + // ANONYMOUS auth is being used, because it unconditionally returns + // AUTHENTICATION_FAILURE if there are no credentials and no loginCallback + function callLogin(provider) { + var errorObject; + + // Use the login callback if we are passed one + // NOTE: Do we even use logincallback? Remove this??? + if (typeof options.loginCallback !== 'undefined') { + options.loginCallback() + .then(function (result) { + try { + provider.login(result.username, result.password) + .then(loginHandler, sessionRejectHandler); + } catch (e) { + sessionRejectHandler( + provider, + progress.data.Session.GENERAL_FAILURE, + { + errorObject: e + } + ); + } + }, callbackRejectHandler); + } else if (options.username && options.password) { + try { + provider.login(options.username, options.password) + .then(loginHandler, sessionRejectHandler); + } catch (e) { + sessionRejectHandler( + provider, + progress.data.Session.GENERAL_FAILURE, + { + errorObject: e + } + ); + } + } else { + // getSession(): The login method was not executed because no credentials were supplied. + errorObject = new Error(progress.data._getMsgText( + "jsdoMSG052", + "getSession()" + )); + sessionRejectHandler( + provider, + progress.data.Session.AUTHENTICATION_FAILURE, + { + // including an Error object to make clear why there is no xhr (normally there would + // be one for an authentication failure) + errorObject: errorObject + } + ); + } + } + + if (typeof options !== 'object') { + // getSession(): 'options' must be of type 'object' + throw new Error(progress.data._getMsgText( + "jsdoMSG503", + "getSession()", + "options", + "object" + )); + } + + if (typeof options.loginCallback !== 'undefined' && + typeof options.loginCallback !== 'function') { + // getSession(): 'options.loginCallback' must be of type 'function' + throw new Error(progress.data._getMsgText( + "jsdoMSG503", + "getSession()", + "options.loginCallback", + "function" + )); + } + + // Create the AuthenticationProvider and let it handle the argument parsing + try { + // If authenticationURI is not set, use serviceURI (except for SSO) + // Note: the test will of course catch any value that evaluates to false, not just undefined or + // null (which are the main concern), but that's probably OK + if (options.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { + if (!options.authenticationURI || !options.authProviderAuthenticationModel) { + // "progress.data.getSession: If the getSession method is passed AUTH_TYPE_SSO as + // the authenticationModel, it must also be passed an authenticationURI and an + // authProviderAuthenticationModel." + throw new Error(progress.data._getMsgText("jsdoMSG509")); + } + } + + if (options.authenticationURI) { + authProviderInitObject.uri = options.authenticationURI; + authProviderInitObject.authenticationModel = options.authProviderAuthenticationModel; + + // if auth uri has been passed, there must be an authProviderAuthenticationModel + if (typeof authProviderInitObject.authenticationModel !== "string") { + // JSDOSession: The 'object' parameter passed to the 'getSession' function + // has an invalid value for the 'authProviderAuthenticationModel' property. + throw new Error(progress.data._getMsgText( + "jsdoMSG502", + "progress.data.getSession", + "object", + "getSession", + "authProviderAuthenticationModel" + )); + } + } else { + authProviderInitObject.uri = options.serviceURI; + authProviderInitObject.authenticationModel = options.authenticationModel; + } + + authProvider = new progress.data.AuthenticationProvider(authProviderInitObject); + options.authProvider = authProvider; + + if (authProvider.hasClientCredentials()) { + loginHandler(authProvider); + } else { + // If model is anon, just log in. + if (authProvider.authenticationModel === progress.data.Session.AUTH_TYPE_ANON) { + authProvider.login() + .then(loginHandler, sessionRejectHandler); + } else { + // We need to log-in with credentials. + callLogin(authProvider); + } + } + } catch (error) { + // throw error; + sessionRejectHandler( + null, + progress.data.Session.GENERAL_FAILURE, + { + errorObject: error + } + ); + } + + return deferred.promise(); + }; + + progress.data.invalidateAllSessions = function () { + var jsdosession, + key, + deferred = $.Deferred(), + jsdosessions = progress.data.ServicesManager._jsdosessions, + invalidatePromises = []; + + for (key in jsdosessions) { + if (jsdosessions.hasOwnProperty(key)) { + jsdosession = jsdosessions[key]; + + invalidatePromises.push(jsdosession.invalidate()); + } + } + + $.when.apply($, invalidatePromises) + .then(function () { + deferred.resolve(progress.data.Session.SUCCESS); + }, function (session, result, info) { + deferred.reject(progress.data.Session.GENERAL_FAILURE, info); + }); + + // Using beautiful jquery shenanigans + return deferred.promise(); + }; + +})(); + +if (typeof exports !== "undefined") { + exports.progress = progress; +} + +//# sourceURL=progress.jsdo.js +/* +progress.auth.js Version: 4.4.0-3 + +Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + "use strict"; // note that this makes JSLint complain if you use arguments[x] + + /*global progress : true*/ + /*global $ : false, storage, XMLHttpRequest*/ + + /* define these if not defined yet - they may already be defined if + progress.js was included first */ + if (typeof progress === "undefined") { + progress = {}; + } + if (typeof progress.data === "undefined") { + progress.data = {}; + } + + // This is really more along the lines of a Factory method in that it explicitly creates an object + // and returns it based on the the authModel parameter (rather than following the default JS + // pattern of adding properties to the "this" object created for it and passed in by the runtime). + // NOTE: If we support multiple AuthenticationProviders that get different tokens from the same + // server, we may need to add a "name" property to the initObject to use as a storage key + + progress.data.AuthenticationProvider = function (initObject) { + var authProv, + authModel, + uri; + + // process constructor arguments + if (typeof initObject === 'object') { + + // these 2 calls throw an appropriate error if the check doesn't pass + this._checkStringArg( + "constructor", + initObject.authenticationModel, + "initObject.authenticationModel", + "initObject.authenticationModel" + ); + + this._checkStringArg( + "constructor", + initObject.uri, + "init-object.uri", + "init-object.uri" + ); + } else { + // AuthenticationProvider: Invalid signature for constructor. The init-object argument + // was missing or invalid. + throw new Error(progress.data._getMsgText( + "jsdoMSG033", + "AuthenticationProvider", + "the constructor", + "The init-object argument was missing or invalid." + )); + } + + authModel = initObject.authenticationModel.toLowerCase(); + switch (authModel) { + case progress.data.Session.AUTH_TYPE_ANON: + this._initialize(initObject.uri, progress.data.Session.AUTH_TYPE_ANON, + {"_loginURI": progress.data.AuthenticationProvider._homeLoginURIBase}); + authProv = this; + break; + case progress.data.Session.AUTH_TYPE_BASIC: + authProv = new progress.data.AuthenticationProviderBasic(initObject.uri); + break; + case progress.data.Session.AUTH_TYPE_FORM: + authProv = new progress.data.AuthenticationProviderForm(initObject.uri); + break; + case progress.data.Session.AUTH_TYPE_FORM_SSO: + authProv = new progress.data.AuthenticationProviderSSO(initObject.uri); + break; + default: + // AuthenticationProvider: The 'init-object' parameter passed to the 'constructor' function + // has an invalid value for the 'authenticationModel' property. + throw new Error(progress.data._getMsgText( + "jsdoMSG502", + "AuthenticationProvider", + "init-object", + "constructor", + "authenticationModel" + )); + //break; + } + + return authProv; + }; + + + // ADD METHODS TO THE AuthenticationProvider PROTOYPE + + // GENERIC IMPLEMENTATION FOR login METHOD THAT THE API IMPLEMENTATIONS OF login CAN CALL + // (technically, they don't override it, they each have small login methods that call this) + progress.data.AuthenticationProvider.prototype._loginProto = + function (sendParam) { + var deferred = $.Deferred(), + xhr, + uriForRequest, + header, + that = this; + + if (this._loggedIn) { + // "The login method was not executed because the AuthenticationProvider is + // already logged in." + throw new Error(progress.data._getMsgText("jsdoMSG051", "AuthenticationProvider")); + } + + xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // process the response from the Web application + that._processLoginResult(xhr, deferred); + } + }; + + if (progress.data.Session._useTimeStamp) { + uriForRequest = progress.data.Session._addTimeStampToURL(this._loginURI); + } else { + uriForRequest = this._loginURI; + } + + this._openLoginRequest(xhr, uriForRequest); + + // We specify application/json for the response so that, if a bad request is sent, an + // OE Web application will directly send back a 401 with error info in the body as JSON. + // So we force the accept header to application/json because if we make an anonymous + // request to a FORM/BASIC backend, it might redirect us to a login page since we have + // no credentials. And since we can technically access JUST the login page, the XHR + // will identify it as SUCCESS. If we specify "application/json", no redirects will + // happen, just a plain old "401 GET OUTTA HERE" code. + xhr.setRequestHeader("Accept", "application/json"); + + xhr.send(sendParam); + return deferred.promise(); + }; + + + + // PUBLIC METHODS (and their "helpers") (documented as part of the JSDO library API) + + // login API method -- just a shell that calls loginProto + progress.data.AuthenticationProvider.prototype.login = function () { + return this._loginProto(); + }; + + // HELPER FOR login METHOD, PROBABLY OVERRIDDEN IN MOST CONSTRUCTORS + progress.data.AuthenticationProvider.prototype._openLoginRequest = function (xhr, uri) { + xhr.open('GET', uri, true); + progress.data.Session._setNoCacheHeaders(xhr); + }; + + // HELPER FOR login METHOD, PROBABLY OVERRIDDEN IN MOST CONSTRUCTORS + progress.data.AuthenticationProvider.prototype._processLoginResult = function (xhr, deferred) { + var result; + + if (xhr.status === 200) { + // Need to set loggedIn now so we can call logout from here if there's an + // error processing the response (e.g., authentication succeeded but we didn't get a + // token for some reason) + this._loggedIn = true; + this._storeInfo(); + result = progress.data.Session.SUCCESS; + } else if (xhr.status === 401) { + // If this is Anonymous, somebody gave us the wrong authenticationModel! + result = progress.data.Session.AUTHENTICATION_FAILURE; + } else { + result = progress.data.Session.GENERAL_FAILURE; + } + + this._settlePromise(deferred, result, {"xhr": xhr}); + }; + + + // logout API METHOD -- SOME CONSTRUCTORS OR PROTOTYPES WILL OVERRIDE THIS + progress.data.AuthenticationProvider.prototype.logout = function () { + var deferred = $.Deferred(); + + this._reset(); + deferred.resolve(this, progress.data.Session.SUCCESS, {}); + return deferred.promise(); + }; + + // hasClientCredentials API METHOD -- PROBABLY ONLY OVERRIDDEN BY SSO + progress.data.AuthenticationProvider.prototype.hasClientCredentials = function () { + return this._loggedIn; + }; + + // hasRefreshToken API METHOD -- returns false for all AutghenticationProvider types except SSO, + // which overrides it + progress.data.AuthenticationProvider.prototype.hasRefreshToken = function () { + return false; + }; + + // QUASI-PUBLIC METHOD + + // general-purpose method for opening requests (mainly for jsdo calls) + // This method is not part of the documented API that a developer would + // program against, but it gets used in a validation check by the JSDOSESSION, because the + // JSDOSESSION code expects it to be present. The point here is that if a developer were to + // create their own AuthenticationProvider object, it would need to include this method + // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change + // it to use promises + progress.data.AuthenticationProvider.prototype._openRequestAndAuthorize = function (xhr, + verb, + uri, + async, + callback) { + var errorObject; + + if (this.hasClientCredentials()) { + xhr.open(verb, uri, async); + + // Check out why we do this in _loginProto + xhr.setRequestHeader("Accept", "application/json"); + } else { + // AuthenticationProvider: The AuthenticationProvider is not managing valid credentials. + errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); + } + + callback(errorObject); + }; + + // GENERAL PURPOSE "INTERNAL" METHODS, NOT RELATED TO SPECIFIC API ELEMENTS + // (not documented, intended for use only within the JSDO library) + + // General purpose method for initializing an object + progress.data.AuthenticationProvider.prototype._initialize = function (uriParam, + authModel, + targetURIs) { + var tempURI, + target; + + Object.defineProperty(this, 'uri', + { + get: function () { + return this._uri; + }, + enumerable: true + }); + + Object.defineProperty(this, 'authenticationModel', + { + get: function () { + return this._authenticationModel; + }, + enumerable: true + }); + + + // get rid of trailing '/' because appending service url that starts with '/' + // will cause request failures + if (uriParam[uriParam.length - 1] === "/") { + tempURI = uriParam.substring(0, uriParam.length - 1); + } else { + tempURI = uriParam; + } + + // take the modified authentication uri and prepend it to all of the targets passed + // in. E.g., the targetURIs object will include a "loginURI" property that has the + // uri segment which is to be added to the auth uri for logging in + for (target in targetURIs) { + if (targetURIs.hasOwnProperty(target)) { + this[target] = tempURI + targetURIs[target]; + } + } + + this._authenticationModel = authModel; + this._uri = uriParam; // keep the uri property the same as what was passed in + + this._loggedIn = false; + this._dataKeys = { + uri: ".uri", + loggedIn: ".loggedIn" + }; + + // future: for page refresh -- storeSessionInfo("authenticationModel", authenticationModel); + + if (typeof sessionStorage === "undefined") { + // "AuthenticationProvider: No support for sessionStorage." + throw new Error(progress.data._getMsgText("jsdoMSG126", + "AuthenticationProvider", + "sessionStorage")); + } + // if you switch to a different type of storage, change the error message argument above + this._storage = sessionStorage; + + // maybe should come up with something more intelligent than this + this._storageKey = this._uri; // or name + this._dataKeys.uri = this._storageKey + this._dataKeys.uri; + this._dataKeys.loggedIn = this._storageKey + this._dataKeys.loggedIn; + + if (this._retrieveLoggedIn()) { + this._loggedIn = true; + } + }; + + + // Store data in storage with the uri as the key. setItem() throws. (Should add an + // option for the developer to specify the key) + // a "QuotaExceededError" error if there is insufficient storage space or + // "the user has disabled storage for the site" (Web storage spec at WHATWG) + progress.data.AuthenticationProvider.prototype._storeInfo = function () { + this._storage.setItem(this._dataKeys.uri, JSON.stringify(this._uri)); + this._storage.setItem(this._dataKeys.loggedIn, JSON.stringify(this._loggedIn)); + }; + + // Get a piece of state data from storage. Returns null if the item isn't in storage + progress.data.AuthenticationProvider.prototype._retrieveInfoItem = function (propName) { + var jsonStr = this._storage.getItem(propName), + value = null; + + if (jsonStr !== null) { + try { + value = JSON.parse(jsonStr); + } catch (e) { + value = null; + } + } + return value; + }; + + // Get an AuthenticationProvider's uri from storage + progress.data.AuthenticationProvider.prototype._retrieveURI = function () { + return this._retrieveInfoItem(this._dataKeys.uri); + }; + + // Get an AuthenticationProvider's logon status from storage + progress.data.AuthenticationProvider.prototype._retrieveLoggedIn = function () { + return this._retrieveInfoItem(this._dataKeys.loggedIn); + }; + + // Clear the persistent storage used by an AuthenticationProvider + progress.data.AuthenticationProvider.prototype._clearInfo = function (info) { + this._storage.removeItem(this._dataKeys.uri); + this._storage.removeItem(this._dataKeys.loggedIn); + }; + + // Put the internal state back to where it is when the constructor finishes + // running (so the authentication model and uri are not changed, but other data is reset. + // and storage is cleared out) + progress.data.AuthenticationProvider.prototype._reset = function () { + this._clearInfo(); + this._loggedIn = false; + }; + + + // General purpose utility method, no overrides expected + progress.data.AuthenticationProvider.prototype._settlePromise = function (deferred, result, info) { + if (result === progress.data.Session.SUCCESS) { + deferred.resolve(this, result, info); + } else { + deferred.reject(this, result, info); + } + }; + + // General purpose utility method, no overrides expected + progress.data.AuthenticationProvider.prototype._checkStringArg = function (fnName, + argToCheck, + argPosition, + argName) { + // TODO: ? distinguish between undefined (so we can give developer a clue that they + // may be missing a property) and defined but wrong type + if (typeof argToCheck !== "string") { + // AuthenticationProvider: Argument {param-position} must be of type string in {fnName} call. + throw new Error(progress.data._getMsgText( + "jsdoMSG121", + "AuthenticationProvider", + argPosition, + "string", + fnName + )); + } else if (argToCheck.length === 0) { + // AuthenticationProvider: {param-name} cannot be an empty string. + throw new Error(progress.data._getMsgText( + "jsdoMSG501", + "AuthenticationProvider", + argName, + fnName + )); + } + }; + + + // "STATIC" PROPERTIES AND METHODS -- not on the prototype -- you cannot access these through an + // object created by "new" --- they are properties of the AuthenticationProvider constructor function + + // Takes an XHR as an input. If the xhr status is 401 (Unauthorized), determines whether + // the auth failure was due to an expired token. Returns progress.data.Session.EXPIRED_TOKEN + // if it was, progress.data.Session.AUTHENTICATION_FAILURE if it wasn't, null if the xhr status wasn't 401 + progress.data.AuthenticationProvider._getAuthFailureReason = function (xhr) { + var contentType, + jsonObject, + result = progress.data.Session.AUTHENTICATION_FAILURE; + + if (xhr.status === 401) { + contentType = xhr.getResponseHeader("Content-Type"); + if (contentType && (contentType.indexOf("application/json") > -1) && xhr.responseText) { + jsonObject = JSON.parse(xhr.responseText); + if (jsonObject.error === "sso.token.expired_token") { + result = progress.data.Session.EXPIRED_TOKEN; + } + } + } else { + result = null; + } + return result; + }; + + Object.defineProperty(progress.data.AuthenticationProvider, '_homeLoginURIBase', { + value: "/static/home.html", + enumerable: true + }); + Object.defineProperty(progress.data.AuthenticationProvider, '_springLoginURIBase', { + value: "/static/auth/j_spring_security_check", + enumerable: true + }); + Object.defineProperty(progress.data.AuthenticationProvider, '_springLogoutURIBase', { + value: "/static/auth/j_spring_security_logout", + enumerable: true + }); + Object.defineProperty(progress.data.AuthenticationProvider, '_springFormTokenLoginURIBase', { + value: progress.data.AuthenticationProvider._springLoginURIBase + "?OECP=yes", + enumerable: true + }); + Object.defineProperty(progress.data.AuthenticationProvider, '_springFormTokenRefreshURIBase', { + value: "/static/auth/token?op=refresh", + enumerable: true + }); + +}()); + +//# sourceURL=progress.jsdo.js +/* +progress.auth.basic.js Version: 4.4.0-3 + +Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + "use strict"; // note that this makes JSLint complain if you use arguments[x] + + /*global progress : true*/ + /*global $ : false, storage, XMLHttpRequest, msg, btoa*/ + + progress.data.AuthenticationProviderBasic = function (uri) { + var defaultiOSBasicAuthTimeout, // TO DO: need to implement the use of this + userName = null, + password = null, + fn; + + // process constructor arguments, etc. + this._initialize(uri, progress.data.Session.AUTH_TYPE_BASIC, + {"_loginURI": progress.data.AuthenticationProvider._homeLoginURIBase}); + + // PRIVATE FUNCTIONS + + // from http://coderseye.com/2007/how-to-do-http-basic-auth-in-ajax.html + function make_basic_auth_header(user, pw) { + var tok = user + ':' + pw, + hash = btoa(tok); + return "Basic " + hash; + } + + // "INTERNAL" METHODS + // Override the protoype's method but call it from within the override + // (Define the override here in the constructor so it has access to instance variables) + this._reset = function () { + userName = null; + password = null; + progress.data.AuthenticationProviderBasic.prototype._reset.apply(this); + }; + + // Override the protoype's method (this method does not invoke the prototype's copy) + // (Define the override here in the constructor so it has access to instance variables) + this._openLoginRequest = function (xhr, uri) { + var auth; + + xhr.open("GET", uri, true); // but see comments below inside the "if userName" + // may have to go with that approach + + if (userName) { + + // set Authorization header + auth = make_basic_auth_header(userName, password); + xhr.setRequestHeader('Authorization', auth); + } + + progress.data.Session._setNoCacheHeaders(xhr); + }; + + // Override the protoype's method but call it from within the override + // (Define the override here in the constructor so it has access to instance variables) + this._processLoginResult = function _basic_processLoginResult(xhr, deferred) { + progress.data.AuthenticationProviderBasic.prototype._processLoginResult.apply( + this, + [xhr, deferred] + ); + if (!this._loggedIn) { + // login failed, clear the credentials + userName = null; + password = null; + } + }; + + // Override the protoype's method (this method does not invoke the prototype's copy, but + // calls a prototype general-purpose login method) + // (Define the override here in the constructor so it has access to instance variables) + this.login = function (userNameParam, passwordParam) { + // these throw if the check fails (may want to do something more elegant) + this._checkStringArg("login", userNameParam, 1, "userName"); + this._checkStringArg("login", passwordParam, 2, "password"); + + userName = userNameParam; + password = passwordParam; + return this._loginProto(); + }; + + // Override the protoype's method (this method does not invoke the prototype's copy) + // (Define the override here in the constructor so it has access to instance variables) + // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change + // it to use promises + this._openRequestAndAuthorize = function (xhr, verb, uri, async, callback) { + var auth, + errorObject; + + if (this.hasClientCredentials()) { + + xhr.open(verb, uri, async); // but see comments below inside the "if userName" + // may have to go with that approach + + if (userName) { + + // set Authorization header + auth = make_basic_auth_header(userName, password); + xhr.setRequestHeader('Authorization', auth); + } + + progress.data.Session._setNoCacheHeaders(xhr); + } else { + // AuthenticationProvider: The AuthenticationProvider is not managing valid credentials. + errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); + } + + callback(errorObject); + }; + }; + + + // Give this constructor the prototype from the "base" AuthenticationProvider + // Do this indirectly by way of an intermediate object so changes to the prototype ("method overrides") + // don't affect other types of AuthenticationProviders that use the prototype) + function BasicProxy() {} + BasicProxy.prototype = progress.data.AuthenticationProvider.prototype; + progress.data.AuthenticationProviderBasic.prototype = new BasicProxy(); + + // Reset the prototype's constructor property so it points to AuthenticationProviderForm rather than + // the one that it just inherited (this is pretty much irrelevant though - the correct constructor + // will get called regardless) + progress.data.AuthenticationProviderBasic.prototype.constructor = + progress.data.AuthenticationProviderBasic; + + + // OVERRIDE METHODS ON PROTOTYPE IF NECESSARY AND POSSIBLE + // (SOME METHODS ARE OVERRIDDEN IN THE CONSTRUCTOR BECAUSE THEY NEED ACCESS TO INSTANCE VARIABLES) + + // NOTE: There are no overrides of the following methods (either here or in the constructor). + // This object uses these methods from the original prototype(i.e., the implementations from the + // AuthenticationProvider object): + // logout (API method) + // hasClientCredentials (API method) + +}()); +//# sourceURL=progress.jsdo.js +/* +progress.auth.form.js Version: 4.4.0-3 + +Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + "use strict"; // note that this makes JSLint complain if you use arguments[x] + + /*global progress : true*/ + /*global $ : false, XMLHttpRequest*/ + + var fn; + + progress.data.AuthenticationProviderForm = function (uri) { + + // PROCESS CONSTRUCTOR ARGUMENTS, CREATE API PROPERTIES, ETC. + this._initialize(uri, progress.data.Session.AUTH_TYPE_FORM, + {"_loginURI": progress.data.AuthenticationProvider._springLoginURIBase, + "_logoutURI": progress.data.AuthenticationProvider._springLogoutURIBase + }); + }; + + // Start by giving this constructor the prototype from the "base" AuthenticationProvider + // Do this indirectly by way of an intermediate object so changes to the prototype ("method overrides") + // don't affect other types of AuthenticationProviders that use the prototype) + function FormProxy() {} + FormProxy.prototype = progress.data.AuthenticationProvider.prototype; + progress.data.AuthenticationProviderForm.prototype = + new FormProxy(); + + // Reset the prototype's constructor property so it points to AuthenticationProviderForm rather than + // the one that it just inherited (this is pretty much irrelevant though - the correct constructor + // will get called regardless) + progress.data.AuthenticationProviderForm.prototype.constructor = + progress.data.AuthenticationProviderForm; + + + // OVERRIDE THE "BASE" AuthenticationProvider PROTOYPE METHODS WHERE NECESSARY + + // All of the methods defined here as part of the AuthenticationProviderForm prototype (instead + // of in the AuthenticationProviderForm constructor) can be inherited by the AuthenticationProviderSSO + // prototype without incurring the overhead of creating a full-blown instance of + // AuthenticationProviderForm to serve as the prototype for the SSO constructor. + // Note: if it turns out that any of the methods defined this way need access to internal variables + // of an AuthenticationProviderForm object, they'll need to be moved out of here. + + // NOTE: There are no overrides of the following methods (either here or in the constructor). + // This object uses these methods from the original prototype(i.e., the implementations from the + // AuthenticationProvider object): + // _reset (general-purpose helper) + // hasClientCredentials (API method) + // _processLoginResult (API helper method) + + + // login API METHOD AND "HELPERS" + progress.data.AuthenticationProviderForm.prototype.login = function (userNameParam, passwordParam) { + var deferred = $.Deferred(), + xhr, + that = this; + + // these throw if the check fails (may want to do something more elegant) + this._checkStringArg("login", userNameParam, 1, "userName"); + this._checkStringArg("login", passwordParam, 2, "password"); + + return this._loginProto("j_username=" + encodeURIComponent(userNameParam) + + "&j_password=" + encodeURIComponent(passwordParam) + "&submit=Submit"); + }; + + // login helper + // Override the protoype's method (this method does not invoke the prototype's copy) + // By defining this here, we can have the SSO AuthenticationProvider use it without + // incurring the overhead of creating a Form instance as the prototype for the SSO constructor + progress.data.AuthenticationProviderForm.prototype._openLoginRequest = function (xhr, uri) { + + xhr.open('POST', uri, true); + + xhr.setRequestHeader("Cache-Control", "max-age=0"); + xhr.setRequestHeader("Pragma", "no-cache"); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + + xhr.withCredentials = true; + + }; + + // logout API METHOD AND "HELPERS" + // Override the prototype method and do not call it because the Anonymous AuthenticationProvider + // doesn't make a server call for logout + // (But this method does do what the SSO AuthenticationProvider needs, so keep it on + // the Form prototype if possible) + progress.data.AuthenticationProviderForm.prototype.logout = function () { + var deferred = $.Deferred(), + xhr, + that = this; + + if (!this._loggedIn) { + // logout is regarded as a success if the AuthenticationProvider isn't logged in + deferred.resolve(this, progress.data.Session.SUCCESS, {}); + } else { + xhr = new XMLHttpRequest(); + this._openLogoutRequest(xhr); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // process the response from the Web application + that._processLogoutResult(xhr, deferred); + } + }; + + xhr.send(); + } + + // Unconditionally reset --- even if the actual server request fails, we still want + // to reset this AuthenticationProvider so it can try a login if desired. + // We also reset even in the case where we're not logged in, just in case. + // (In the future we can add a parameter that controls whether the reinit is unconditional, + // if the developer wants to log out of the token server session but contnue to use the token) + this._reset(); + return deferred.promise(); + }; + + // logout helper (there is no version defined in the original protoype) + progress.data.AuthenticationProviderForm.prototype._openLogoutRequest = function (xhr) { + xhr.open('GET', this._logoutURI, true); + xhr.setRequestHeader("Cache-Control", "max-age=0"); + xhr.withCredentials = true; + xhr.setRequestHeader("Accept", "application/json"); + }; + + // logout helper (there is no version defined in the original protoype) + progress.data.AuthenticationProviderForm.prototype._processLogoutResult = function (xhr, deferred) { + var result; + + if (xhr.status === 200) { + result = progress.data.Session.SUCCESS; + } else if (xhr.status === 401) { + // treat this as a success because the most likely cause is that the session expired + // (Note that an 11.7 OE PAS Web application will return a 200 if we log out with + // an expired JSESSIONID, so this code may not be executed anyway) + result = progress.data.Session.SUCCESS; + } else { + result = progress.data.Session.GENERAL_FAILURE; + } + + this._settlePromise(deferred, result, {"xhr": xhr}); + + }; + + // GENERAL PURPOSE METHOD FOR OPENING REQUESTS (MAINLY FOR JSDO CALLS) + // Override the protoype's method but call it from within the override + // Since the override is being put into the constructor's prototype, and + // since it calls the overridden method which had originally been in the prototype, + // we add a "_super" property to the overriding method so it can still access the original method + // (We could just call that method directly in here like this: + // progress.data.AuthenticationProviderProto.prototype._openRequestAndAuthorize + // but if we ever change the place where we get the initial protoype for + // AuthenticationProviderForm from, we would need to remember to change that here. + // The use of the _super property will handle that automatically, plus it was more fun + // to do it this way) + // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change + // it to use promises + fn = progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize; + progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize = + function (xhr, verb, uri, async, callback) { + + function afterSuper(errorObject) { + xhr.withCredentials = true; + callback(errorObject); + } + + try { + progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize._super.apply( + this, + [xhr, verb, uri, async, afterSuper] + ); + } catch (e) { + callback(e); + } + }; + progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize._super = fn; + + +}()); +//# sourceURL=progress.jsdo.js +/* +progress.auth.sso.js Version: 4.4.0-3 + +Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +(function () { + + "use strict"; // note that this makes JSLint complain if you use arguments[x] + + /*global progress : true*/ + /*global $ : false */ + + var fn; + +// ADD AN OPTIONS PARAM THAT CAN INCLUDE A NAME FOR PAGE REFRESH? + progress.data.AuthenticationProviderSSO = function (uri) { + var that = this, + // SSO specific + _automaticTokenRefresh, + temp, + ssoTokenInfo = null, + tokenDataKeys = { // SSO specific + token: ".access_token", + refreshToken: ".refresh_token", + tokenType: ".token_type", + expiration: ".expires_in", + accessTokenExpiration: ".accessTokenExpiration" + }; + + // PRIVATE FUNCTIONS + // (The constructor uses local variables and functions mainly to try to protect the token + // information as much as possible. A few could probably be defined as properties/methods, but + // there's currently no need for that because the AuthenticationProvider API has no objects + // that inherit from AuthenticationProviderSSO.) + + // Store the given token with the uri as the key. setItem() throws + // a "QuotaExceededError" error if there is insufficient storage space or + // "the user has disabled storage for the site" (Web storage spec at WHATWG) + function storeTokenInfo(info) { + var date, + accessTokenExpiration; + + if (info.access_token.length) { + that._storage.setItem(tokenDataKeys.token, JSON.stringify(info.access_token)); + } + if (info.refresh_token.length) { + that._storage.setItem(tokenDataKeys.refreshToken, JSON.stringify(info.refresh_token)); + // The time given for the access token's expiration is in seconds. We transform it + // into milliseconds and add it to date.getTime() for a more standard format. + date = new Date(); + // This should probably be renamed accessTokenRefreshThreshold + accessTokenExpiration = date.getTime() + (info.expires_in * 1000 * 0.75); + that._storage.setItem(tokenDataKeys.accessTokenExpiration, JSON.stringify(accessTokenExpiration)); + } else { + // if there is no refresh token, remove any existing one. This handles the case where + // we got a new token via refresh, but now we're not being given any more refresh tokens + that._storage.removeItem(tokenDataKeys.refreshToken); + that._storage.removeItem(tokenDataKeys.accessTokenExpiration); + } + that._storage.setItem(tokenDataKeys.tokenType, JSON.stringify(info.token_type)); + that._storage.setItem(tokenDataKeys.expiration, JSON.stringify(info.expires_in)); + } + + // get one of the pieces of data related to tokens from storage (could be the token itself, or + // the refresh token, expiration info, etc.). Returns null if the item isn't in storage + function retrieveTokenProperty(propName) { + var jsonStr = that._storage.getItem(propName), + value = null; + + if (jsonStr !== null) { + try { + value = JSON.parse(jsonStr); + } catch (e) { + value = null; + } + } + return value; + } + + function retrieveToken() { + return retrieveTokenProperty(tokenDataKeys.token); + } + + function retrieveRefreshToken() { + return retrieveTokenProperty(tokenDataKeys.refreshToken); + } + + function retrieveAccessTokenExpiration() { + return retrieveTokenProperty(tokenDataKeys.accessTokenExpiration); + } + + function retrieveTokenType() { + return retrieveTokenProperty(tokenDataKeys.tokenType); + } + + // This is going to be hardcoded for now. This can very + // possibly change in the future if we decide to expose + // the token to the user. + function getToken() { + return retrieveToken(); + } + + function retrieveExpiration() { + return retrieveTokenProperty(tokenDataKeys.expiration); + } + + function clearTokenInfo(info) { + that._storage.removeItem(tokenDataKeys.token); + that._storage.removeItem(tokenDataKeys.refreshToken); + that._storage.removeItem(tokenDataKeys.tokenType); + that._storage.removeItem(tokenDataKeys.expiration); + that._storage.removeItem(tokenDataKeys.accessTokenExpiration); + } + + // function is SSO specific + function openRefreshRequest(xhr) { + xhr.open('POST', that._refreshURI, true); + xhr.setRequestHeader("Cache-Control", "max-age=0"); + xhr.withCredentials = true; + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.setRequestHeader("Accept", "application/json"); + } + + // function is SSO specific + function processRefreshResult(xhr, deferred) { + var errorObject, + result, + ssoTokenJSON; + + if (xhr.status === 200) { + // get token and store it; if that goes well, resolve the promise, otherwise reject it + try { + ssoTokenInfo = JSON.parse(xhr.responseText); + + if (ssoTokenInfo.access_token) { + storeTokenInfo(ssoTokenInfo); + // got the token info, its access_token has a value, and storeTokenInfo() + // didn't thrown an error, so call this a success + result = progress.data.Session.SUCCESS; + } else { + result = progress.data.Session.GENERAL_FAILURE; + // {1}: Unexpected error calling refresh: {error-string} + // ( No token returned from server) + errorObject = new Error(progress.data._getMsgText( + "jsdoMSG049", + "AuthenticationProvider", + "refresh", + progress.data._getMsgText("jsdoMSG050") + )); + } + } catch (ex) { + result = progress.data.Session.GENERAL_FAILURE; + // {1}: Unexpected error calling refresh: {error-string} + // (error could be thrown from storeTokenInfo when it calls setItem()) + errorObject = new Error(progress.data._getMsgText( + "jsdoMSG049", + "AuthenticationProvider", + "refresh", + ex.message + )); + } + } else if (xhr.status === 401) { + that._reset(); // treat authentication failure as the equivalent of a logout + result = progress.data.Session.AUTHENTICATION_FAILURE; + } else { + result = progress.data.Session.GENERAL_FAILURE; + } + + that._settlePromise(deferred, result, {"xhr": xhr, + "errorObject": errorObject}); // OK if undefined + } + + + this._processLoginResult = function (xhr, deferred) { + var errorObject, + result, + ssoTokenJSON; + + if (xhr.status === 200) { + // Need to set loggedIn now so we can call logout from here if there's an + // error processing the response (e.g., authentication succeeded but we didn't get a + // token for some reason) + this._loggedIn = true; + + // get token and store it; if that goes well, resolve the promise, otherwise reject it + try { + ssoTokenInfo = JSON.parse(xhr.responseText); + + if (ssoTokenInfo.access_token) { + storeTokenInfo(ssoTokenInfo); + // got the token info, its access_token has a value, and storeTokenInfo() + // didn't throw an error, so call this a success + result = progress.data.Session.SUCCESS; + } else { + result = progress.data.Session.GENERAL_FAILURE; + // {1}: Unexpected error calling login: {error-string} + // ( No token returned from server) + errorObject = new Error(progress.data._getMsgText( + "jsdoMSG049", + "AuthenticationProvider", + "login", + progress.data._getMsgText("jsdoMSG050") + )); + } + } catch (ex) { + result = progress.data.Session.GENERAL_FAILURE; + // {1}: Unexpected error calling login: {error-string} + // (error could be thrown from storeTokenInfo when it calls setItem()) + errorObject = new Error(progress.data._getMsgText( + "jsdoMSG049", + "AuthenticationProvider", + "login", + ex.message + )); + } + + // log out if there was an error processing the response so the app can try to log in again + if (result !== progress.data.Session.SUCCESS) { + // call logout, but ignore its outcome -- just tell caller that login failed + this.logout() + .always(function (authProv) { + authProv._settlePromise(deferred, result, {"xhr": xhr, + "errorObject": errorObject}); + }); + return; // so we don't execute the reject below, which could invoke the fail handler + // before we're done with the logout + } + + } else if (xhr.status === 401) { + result = progress.data.Session.AUTHENTICATION_FAILURE; + } else { + result = progress.data.Session.GENERAL_FAILURE; + } + + this._settlePromise(deferred, result, {"xhr": xhr}); + }; + + + // Override the protoype's method but call it from within the override. (Define the override + // here in the constructor so it has access to the internal function and variable) + this._reset = function () { + progress.data.AuthenticationProviderSSO.prototype._reset.apply(this); + clearTokenInfo(); + ssoTokenInfo = null; + }; + + + // Override the protoype's method but call it from within the override. (Define the override + // here in the constructor so it has access to the internal function getToken() ) + // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change + // it to use promises + this._openRequestAndAuthorize = function (xhr, + verb, + uri, + async, + callback) { + var that = this, + date, + errorObject; + + function afterRefreshCheck(provider, result, info) { + // if refresh failed because of auth failure, we will have gotten rid of the + // token and reset the auth provider + if (result === progress.data.Session.AUTHENTICATION_FAILURE) { + callback(new Error(progress.data._getMsgText("jsdoMSG060"))); + } else { + // We've done the refresh check (and possible refresh) for SSO, now execute + // the base _openRequest... method, which does common things for Form-based + progress.data.AuthenticationProviderSSO.prototype._openRequestAndAuthorize.apply( + that, + [xhr, verb, uri, async, function (errorObject) { + if (!errorObject) { + xhr.setRequestHeader('Authorization', "oecp " + getToken()); + } + callback(errorObject); + }] + ); + } + } + + if (this.hasClientCredentials()) { + // Every token given has an expiration "hint". If the token's lifespan + // is close to or past that limit, then a refresh is done. + // No matter what the outcome of the refresh, keep in mind we always + // send the original request. + date = new Date(); + if (this.automaticTokenRefresh && + this.hasRefreshToken() && + date.getTime() > retrieveAccessTokenExpiration()) { + try { + this.refresh() + .always(function (provider, result, info) { + afterRefreshCheck(provider, result, info); + }); + } catch (e) { + callback(e); + } + } else { + afterRefreshCheck(this, progress.data.Session.SUCCESS, null); + } + } else { + // This message is SSO specific, unless we can come up with a more general message + // JSDOSession: The AuthenticationProvider needs to be managing a valid token. + errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); + callback(errorObject); + } + }; + + + // API METHODS + + // override the prototype's hasClientCredentials method + this.hasClientCredentials = function () { + return (retrieveToken() === null ? false : true); + }; + + + this.refresh = function () { + var deferred = $.Deferred(), + xhr; + + if (!this._loggedIn) { + // "The refresh method was not executed because the AuthenticationProvider is not logged in." + throw new Error(progress.data._getMsgText("jsdoMSG053", "AuthenticationProvider", "refresh")); + } + + if (!this.hasRefreshToken()) { + // "Token refresh was not executed because the AuthenticationProvider does not have a + // refresh token." + throw new Error(progress.data._getMsgText("jsdoMSG054", "AuthenticationProvider")); + } + + xhr = new XMLHttpRequest(); + openRefreshRequest(xhr); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // process the response from the Web application + processRefreshResult(xhr, deferred); + } + }; + + xhr.send('{"token_type":"' + retrieveTokenType() + '","refresh_token":"' + + retrieveRefreshToken() + '"}'); + return deferred.promise(); + }; + + + this.hasRefreshToken = function () { + return (retrieveRefreshToken() === null ? false : true); + }; + + + // PROCESS CONSTRUCTOR ARGUMENTS, CREATE API PROPERTIES, ETC. + this._initialize(uri, + progress.data.Session.AUTH_TYPE_FORM_SSO, + {"_loginURI": progress.data.AuthenticationProvider._springFormTokenLoginURIBase, + "_logoutURI": progress.data.AuthenticationProvider._springLogoutURIBase, + "_refreshURI": progress.data.AuthenticationProvider._springFormTokenRefreshURIBase + }); + + // in addition to the standard AuthenticationProvider properties, an SSO provider also has a property + // to control automatic token refresh (it's enabled by default on the assumption that developers + // will usually want this) + _automaticTokenRefresh = true; + Object.defineProperty(this, 'automaticTokenRefresh', + { + get: function () { + return _automaticTokenRefresh; + }, + set: function (value) { + if (value === true || value === false) { + _automaticTokenRefresh = value; + } else { + throw new Error(progress.data._getMsgText("jsdoMSG061", + "AuthenticationProvider", + "automaticTokenRefresh")); + } + }, + enumerable: true + }); + + // add the automaticTokenRefresh key to the base class's list of data keys + this._dataKeys.automaticTokenRefresh = this._storageKey + ".automaticTokenRefresh"; + // set it from storage, if it's in storage + temp = this._retrieveInfoItem(this._dataKeys.automaticTokenRefresh); + if (temp === false) { + _automaticTokenRefresh = false; + } + + // We're currently storing the token in storage with the + // uri as the key. This is subject to change later. + tokenDataKeys.token = this._storageKey + tokenDataKeys.token; + tokenDataKeys.refreshToken = this._storageKey + tokenDataKeys.refreshToken; + tokenDataKeys.tokenType = this._storageKey + tokenDataKeys.tokenType; + tokenDataKeys.expiration = this._storageKey + tokenDataKeys.expiration; + tokenDataKeys.accessTokenExpiration = this._storageKey + tokenDataKeys.accessTokenExpiration; + + // NOTE: we rely on the prototype's logic to set this._loggedIn. An alternative could be to + // use the presence of a token to determine that, but it's conceivable that we could be + // logged in but for some reason not have a token (e.g., a token expired, or we logged in + // but the authentication server did not return a token) + if (retrieveToken()) { + this._loggedIn = true; + } + + // END OF CONSTRUCTOR PROCESSING + + }; + // END OF AuthenticationProviderSSO CONSTRUCTOR + + // NOTE: This is used only for the SSO authentication. + // Define the prototype as an instance of an AuthenticationProviderForm object + function SSOProxy() {} + SSOProxy.prototype = progress.data.AuthenticationProviderForm.prototype; + progress.data.AuthenticationProviderSSO.prototype = + new SSOProxy(); + + // But reset the constructor back to the SSO constructor (this is pretty much irrelevant, + // though. The correct constructor would be called anyway. It's mainly for the sake of anyone + // wanting to see what the constructor of an object is (maybe a framework) + progress.data.AuthenticationProviderSSO.prototype.constructor = + progress.data.AuthenticationProviderSSO; + + // override the base AuthenticationProvider _storeInfo and _clearinfo, but keep refs so they + // can be invoked within the overrides + fn = progress.data.AuthenticationProviderSSO.prototype._storeInfo; + progress.data.AuthenticationProviderSSO.prototype._storeInfo = + function () { + progress.data.AuthenticationProviderSSO.prototype._storeInfo._super.apply(this); + this._storage.setItem(this._dataKeys.automaticTokenRefresh, + JSON.stringify(this._automaticTokenRefresh)); + }; + progress.data.AuthenticationProviderSSO.prototype._storeInfo._super = fn; + + fn = progress.data.AuthenticationProviderSSO.prototype._clearInfo; + progress.data.AuthenticationProviderSSO.prototype._clearInfo = + function () { + progress.data.AuthenticationProviderSSO.prototype._clearInfo._super.apply(this); + this._storage.removeItem(this._dataKeys.automaticTokenRefresh); + }; + progress.data.AuthenticationProviderSSO.prototype._clearInfo._super = fn; + + + + // NOTE: There are no overrides of the following methods (either here or in the constructor). + // This object uses these methods from the original prototype(i.e., the implementations from the + // Auth...Form object) because for an OE SSO token server, the login/logout model is Form (the + // only difference is the use of a special URI query string in the login (see the call to + // initialize() in the SSO constructor (above)): + // login (API method) + // _openLoginRequest (API helper method) + // logout (API method) + // _openLogoutRequest (API helper method) + // _processLogoutResult (API helper method) + + // NOTE: All overrides are implemented in the constructor (rather than adding them to the prototype) + // because they need access to variables and/or functions that are defined in the constructor + // (in an attempt to protect the token info somewhat) + +}()); + + +/* +progress.data.kendo.js Version: 4.4.0-01 + +Copyright (c) 2015-2017 Progress Software Corporation and/or its subsidiaries or affiliates. + +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. + + */ + +/*global jQuery, kendo, progress*/ +/*jslint nomen: true*/ +/*jslint vars: false*/ +(function () { + + // "use strict"; + + var JSDODataReader, JSDOTransport, JSDOObservable = new kendo.Observable(); + + function initializeJSDO(transport, options) { + var jsdo, resourceName; + + if (options.jsdo instanceof progress.data.JSDO) { + jsdo = options.jsdo; + } else if (typeof (options.jsdo) === "string") { + // Create a new JSDO instance using the specified configuration + resourceName = options.jsdo; + + // Create JSDO + jsdo = new progress.data.JSDO({ name: resourceName }); + } else { + throw new Error("JSDO: jsdo property must be either a JSDO instance or a string."); + } + + if (transport.tableRef === undefined && jsdo._defaultTableRef) { + transport.tableRef = jsdo._defaultTableRef._name; + } + if (transport.tableRef === undefined) { + throw new Error("JSDO: A tableRef must be specified when using a multi-table DataSet."); + } else if (jsdo[transport.tableRef] === undefined) { + throw new Error("JSDO: tableRef '" + transport.tableRef + "' is not present in JSDO definition."); + } + + return jsdo; + } + + + + // This define functions to read the data object from the JSDO DataSource + JSDODataReader = kendo.data.readers.json.extend({ + init: function (arg1) { + // init function + var event = {}, + transport; + + // Query the transport object to obtain a transport reference + JSDOObservable.trigger("info", event); + transport = this.transport = event.sender._events.info.transport; + + kendo.data.readers.json.fn.init.call(this, arg1); + + // Overrides model property after init.call + // because if model is defined at the object level init.call removes it + this.model = kendo.data.Model.define({ + init: function (data) { + var record; + if (!data || jQuery.isEmptyObject(data)) { + data = transport._getInitialValues(); + } + record = transport._convertDataTypes(data); + transport.jsdo._deleteProdsProperties(record, true); + kendo.data.Model.fn.init.call(this, record); + }, + id: "_id", + fields: transport._getModel() + }); + }, + total: function (data) { + return data.total || (data.data ? data.data.length : data.length); + }, + data: function (data) { + return data.data || data; + } + }); + + // This define transport for JSDO DataSource providing implementation for: + // create, read, update, destroy, submit + JSDOTransport = kendo.data.RemoteTransport.extend({ + init: function (options) { + var transport = this, + fnName; + + if (options.tableRef !== undefined) { + transport.tableRef = options.tableRef; + } + transport.jsdo = initializeJSDO(transport, options); + transport._initFromServer = false; + transport.autoSave = options.autoSave !== undefined ? options.autoSave : true; + transport.readLocal = options.readLocal !== undefined ? options.readLocal : false; + transport.countFnName = options.countFnName; + transport.useArrays = options.useArrays !== undefined ? options.useArrays : false; + + if (transport.countFnName !== undefined) { + if (typeof (transport.jsdo[transport.countFnName]) !== "function") { + throw new Error("Invoke operation '" + + transport.countFnName + "' for countFnName is not defined."); + } + } else if (transport.jsdo._resource.generic.count !== undefined) { + for (fnName in transport.jsdo._resource.fn) { + if (transport.jsdo._resource.fn.hasOwnProperty(fnName)) { + if (transport.jsdo._resource.generic.count === transport.jsdo._resource.fn[fnName]["function"]) { + transport.countFnName = fnName; + break; + } + } + } + } + + // Define "info" event to return transport object to reader + JSDOObservable.one("info", function (e) { + e.sender._events.info.transport = transport; + }); + + transport._initConvertTypes(); + + kendo.data.RemoteTransport.fn.init.call(this, options); + }, + _initConvertTypes: function () { + // _initConvertTypes: + // Initializes transport._convertTypes to indicate whether a conversion of the data is needed + // when it is passed to Kendo UI. + // This operation is currently only needed for date fields that are stored as strings. + // Sets array _dateFields to the fields of date fields to convert. + var transport = this, + i, + schema, + fieldName, + dateFields = [], + arrayFields = [], + convertDateFields = false; + + transport._convertTypes = false; + + schema = transport.jsdo[transport.tableRef].getSchema(); + for (i = 0; i < schema.length; i += 1) { + fieldName = schema[i].name; + if (fieldName.length > 0 && fieldName.charAt(0) !== "_") { + if (schema[i].type === "string" && + schema[i].format && + (schema[i].format.indexOf("date") !== -1)) { + dateFields.push(fieldName); + if (!convertDateFields) { + convertDateFields = true; + } + } else if (!transport.useArrays && schema[i].type === "array") { + arrayFields.push(fieldName); + if (!convertDateFields && schema[i].ablType && + schema[i].ablType.indexOf("DATE") === 0) { + convertDateFields = true; + } + } + } + } + + if (dateFields.length > 0 || arrayFields.length > 0) { + transport._convertTypes = true; + // _convertFields: Object containing arrays for each data type to convert + transport._convertFields = {}; + transport._convertFields._arrayFields = []; + transport._convertFields._dateFields = []; + } + if (dateFields.length > 0) { + transport._convertFields._dateFields = dateFields; + } + if (convertDateFields) { + transport._convertFields._datePattern = new RegExp("^([0-9]+)?-([0-9]{2})?-([0-9]{2})?$"); + transport._convertFields._dateTimePattern = new RegExp( + "^([0-9]+)?-([0-9]{2})?-([0-9]{2})?" + + "T([0-9]{2})?:([0-9]{2})?:([0-9]{2})?.([0-9]{3})?$" + ); + } + if (arrayFields.length > 0) { + transport._convertFields._arrayFields = arrayFields; + } + }, + _convertStringToDate: function (data, fieldName, targetFieldName) { + var transport = this, + array, + ablType, + orig; + + if (!targetFieldName) { + targetFieldName = fieldName; + } + // Check if string is -- + array = transport._convertFields._datePattern.exec(data[targetFieldName]) || []; + if (array.length > 0) { + data[targetFieldName] = new Date(parseInt(array[1], 10), + parseInt(array[2], 10) - 1, + parseInt(array[3], 10)); + } else { + ablType = transport.jsdo[transport.tableRef]._fields[fieldName.toLowerCase()].ablType; + if (ablType === "DATETIME") { + array = transport._convertFields._dateTimePattern.exec(data[targetFieldName]) || []; + if (array.length > 0) { + // Convert date to local time zone + data[targetFieldName] = new Date(parseInt(array[1], 10), + parseInt(array[2], 10) - 1, + parseInt(array[3], 10), + parseInt(array[4], 10), + parseInt(array[5], 10), + parseInt(array[6], 10), + parseInt(array[7], 10)); + } + } + + // Check to see if it was converted + if (typeof (data[targetFieldName]) === "string") { + orig = data[targetFieldName]; + try { + data[targetFieldName] = new Date(data[targetFieldName]); + } + catch (e) { + // Conversion to a date object was not successful + data[targetFieldName] = orig; + console.log(msg.getMsgText("jsdoMSG000", + "_convertStringToDate() could not convert to date object: " + orig)); + } + } + } + }, + _convertDataTypes: function (data) { + // _convertDataTypes: + // Converts data types in the specified data record. + // Data record could come from the JSDO or from the Kendo UI DataSource. + // Returns a reference to the record. + // Returns a copy when useArrays is undefined or false. + var transport = this, + i, + k, + fieldName, + schemaInfo, + prefixElement, + elementName, + copy; + + if (!transport.useArrays && transport._convertTypes && (transport._convertFields._arrayFields.length > 0)) { + copy = {}; + transport.jsdo._copyRecord(transport.jsdo._buffers[transport.tableRef], data, copy); + data = copy; + } + + if (!transport._convertTypes) { + return data; + } + + for (k = 0; k < transport._convertFields._arrayFields.length; k += 1) { + fieldName = transport._convertFields._arrayFields[k]; + if (data[fieldName]) { + schemaInfo = transport.jsdo[transport.tableRef]._fields[fieldName.toLowerCase()]; + prefixElement = transport.jsdo._getArrayField(fieldName); + for (i = 0; i < schemaInfo.maxItems; i += 1) { + // ABL arrays are 1-based + elementName = prefixElement.name + (i + 1); + + if (!transport.jsdo[transport.tableRef]._fields[elementName.toLowerCase()]) { + // Skip element if a field with the same name exists + // Extract value from array field into individual field + // Array is removed later + data[elementName] = data[fieldName][i]; + + // Convert string DATE fields to JS DATE + if ((schemaInfo.ablType) && (schemaInfo.ablType.indexOf("DATE") === 0) && (typeof (data[elementName]) === "string")) { + transport._convertStringToDate(data, fieldName, elementName); + } + } + } + if (!transport.useArrays) { + delete data[fieldName]; + } + } + } + + for (k = 0; k < transport._convertFields._dateFields.length; k += 1) { + fieldName = transport._convertFields._dateFields[k]; + if (typeof (data[fieldName]) === "string") { + transport._convertStringToDate(data, fieldName); + } + } + + return data; + }, + _getModel: function () { + var transport = this, + i, + j, + fields = {}, + schema, + value, + type, + element; + + schema = transport.jsdo[transport.tableRef].getSchema(); + for (i = 0; i < schema.length; i += 1) { + // Skip internal fields + if (schema[i].name.length > 0 && schema[i].name.charAt(0) !== "_") { + type = schema[i].type; + if (type === "integer") { + type = "number"; + } else if (type === "string" && + schema[i].format && + schema[i].format.indexOf("date") !== -1) { + // Set type to "date" is type is DATE or DATETIME[-TZ] + type = "date"; + } + if (type === "array") { + for (j = 0; j < schema[i].maxItems; j += 1) { + value = transport.jsdo._getDefaultValue(schema[i]); + element = transport.jsdo._getArrayField(schema[i].name, j); + if (!transport.jsdo[transport.tableRef]._fields[element.name.toLowerCase()]) { + // Skip element if a field with the same name exists + + // Calculate type of array element + type = schema[i].items.type; + if (type === "integer") { + type = "number"; + } else if (type === "string" && schema[i].ablType && (schema[i].ablType.indexOf("DATE") !== -1)) { + type = "date"; + } + + fields[element.name] = {}; + fields[element.name].type = type; + if (value !== undefined) { + fields[element.name].defaultValue = value; + } + } + } + } else { + value = transport.jsdo._getDefaultValue(schema[i]); + fields[schema[i].name] = {}; + fields[schema[i].name].type = type; + if (value !== undefined) { + fields[schema[i].name].defaultValue = value; + } + } + } + } + return fields; + }, + _getInitialValues: function () { + var transport = this, + i, + j, + data = {}, + schema, + defaultValue; + schema = transport.jsdo[transport.tableRef].getSchema(); + for (i = 0; i < schema.length; i += 1) { + // Skip internal fields + if (schema[i].name.length > 0 && schema[i].name.charAt(0) !== "_") { + defaultValue = transport.jsdo._getDefaultValue(schema[i]); + if (schema[i].type === "array") { + data[schema[i].name] = []; + + for (j = 0; j < schema[i].maxItems; j += 1) { + data[schema[i].name][j] = defaultValue; + } + + } else { + data[schema[i].name] = defaultValue; + } + } + } + return data; + }, + _getData: function (options) { + var jsdo = this.jsdo, + data = {}, + params, + array, + filter; + + if (options && options.data) { + params = { + tableRef: this.tableRef, + filter: options.data.filter, + sort: options.data.sort, + skip: options.data.skip, + top: options.data.take + }; + + array = jsdo[this.tableRef].getData({filter: filter}); + data.total = array.length; + array = jsdo[this.tableRef].getData(params); + data.data = array; + } else { + array = jsdo[this.tableRef].getData(); + data.data = array; + data.total = array.length; + } + + return data; + }, + read: function (options) { + try { + var jsdo = this.jsdo, + filter, + data = {}, + transport = this, + callback, + property, + optionsMapping = { + filter: "filter", + take: "top", + skip: "skip", + sort: "sort" + }, + saveUseRelationships; + + if (!this._initFromServer) { + if (jsdo[this.tableRef]._parent) { + this._initFromServer = (jsdo[jsdo[this.tableRef]._parent]._data && (jsdo[jsdo[this.tableRef]._parent]._data.length > 0)) + || (jsdo[this.tableRef]._data instanceof Array && (jsdo[this.tableRef]._data.length > 0)); + } else { + this._initFromServer = (jsdo[this.tableRef]._data instanceof Array) && (jsdo[this.tableRef]._data.length > 0); + } + } + + data.data = []; + if (this.readLocal && this._initFromServer) { + saveUseRelationships = jsdo.useRelationships; + jsdo.useRelationships = false; + data = this._getData(options); + jsdo.useRelationships = saveUseRelationships; + options.success(data); + return; + } + + if (!this.readLocal) { + // readLocal is false or _initFromServer is false + + if (options.data) { + // Only create filter object if options.data contains viable properties + for (property in options.data) { + if (options.data.hasOwnProperty(property)) { + if (options.data[property] !== undefined + && optionsMapping[property]) { + if (filter === undefined) { + filter = {}; + } + filter[optionsMapping[property]] = options.data[property]; + } + } + } + if (filter) { + filter.tableRef = this.tableRef; + } + } + } + + callback = function onAfterFillJSDO(jsdo, success, request) { + var data = {}, saveUseRelationships, promise, total, exception; + + if (success) { + saveUseRelationships = jsdo.useRelationships; + jsdo.useRelationships = false; + + if (transport.readLocal) { + // Use options.data to filter data + data = transport._getData(options); + } else { + data.data = jsdo[transport.tableRef].getData(); + + total = jsdo.getProperty("server.count"); + if (total) { + data.total = total; + } + + } + jsdo.useRelationships = saveUseRelationships; + transport._initFromServer = true; + if (options.data && options.data.take) { + if (!transport.readLocal && + transport.countFnName !== undefined && + typeof (jsdo[transport.countFnName]) === "function") { + + if (options.data.skip === 0 && options.data.take > data.data.length) { + options.success(data); + return; + } + + // Reuse filter string from the request.objParam object from fill() call. + promise = jsdo.invoke( + transport.countFnName, + { filter: request.objParam.filter } + ); + /*jslint unparam: true*/ + promise.done(function (jsdo, success, request) { + var exception, total; + + try { + if (typeof (request.response) === "object" && + Object.keys(request.response).length === 1) { + total = request.response[Object.keys(request.response)]; + if (typeof (total) !== "number") { + // Use generic exception if data type is not a number. + total = undefined; + } + } + } catch (e) { + // This exception is ignored a generic exception is used later. + } + if (total !== undefined) { + if (total) { + data.total = total; + } + options.success(data); + } else { + exception = new Error("Unexpected response from '" + + transport.countFnName + "' operation."); + options.error(request.xhr, request.xhr.status, exception); + } + }); + promise.fail(function (jsdo, success, request) { + var exception; + exception = new Error("Error invoking '" + + transport.countFnName + "' operation."); + options.error(request.xhr, request.xhr.status, exception); + }); + /*jslint unparam: false*/ + } else { + options.success(data); + } + } else { + options.success(data); + } + } else { + exception = new Error("Error while reading records."); + options.error(request.xhr, request.xhr.status, exception); + } + }; + + jsdo.fill(filter).done(callback).fail(callback); + + } catch (e) { + options.error(null, null, e); + } + }, + _processChanges: function (options, request) { + var jsdo = this.jsdo, + transport = this, + array, + i, + jsrecord, + id, + record; + + if (options.batch) { + array = []; + if (options.data.created instanceof Array) { + for (i = 0; i < options.data.created.length; i += 1) { + jsrecord = jsdo[transport.tableRef].findById( + options.data.created[i]._id + ); + if (jsrecord) { + record = transport._convertDataTypes(jsrecord.data); + array.push(record); + } else if (jsdo.autoApplyChanges) { + options.error( + null, + null, + new Error("Created record was not found in memory.") + ); + return; + } + } + } + options.success(array, "create"); + + array = []; + if (options.data.updated instanceof Array) { + for (i = 0; i < options.data.updated.length; i += 1) { + jsrecord = jsdo[transport.tableRef].findById( + options.data.updated[i]._id + ); + if (jsrecord) { + record = transport._convertDataTypes(jsrecord.data); + array.push(record); + } else if (jsdo.autoApplyChanges) { + options.error( + null, + null, + new Error("Updated record not found in memory.") + ); + return; + } + } + } + options.success(array, "update"); + + array = []; + if (options.data.destroyed instanceof Array) { + for (i = 0; i < options.data.destroyed.length; i += 1) { + jsrecord = jsdo[transport.tableRef].findById( + options.data.destroyed[i]._id + ); + if (jsrecord && jsdo.autoApplyChanges) { + options.error( + null, + null, + new Error("Deleted record was found in memory.") + ); + return; + } + } + } + options.success(array, "destroy"); + } else { + if (jsdo._resource.idProperty) { + if (request + && request.batch + && request.batch.operations instanceof Array + && request.batch.operations.length === 1) { + id = request.batch.operations[0].jsrecord.data._id; + } + } else { + id = options.data._id; + } + jsrecord = jsdo[transport.tableRef].findById(id); + if (jsrecord) { + record = transport._convertDataTypes(jsrecord.data); + options.success(record); + } else { + options.success({}); + } + } + }, + _saveChanges: function (options) { + var transport = this, + callback = function onAfterSaveChanges(jsdo, success, request) { + var jsrecord, + xhr, + status, + exception; + + if (success) { + // _id is expected to be set for CUD operations + // Deleted records will not be found and data is expected to be undefined + transport._processChanges(options, request); + } else { + if (request.batch + && request.batch.operations instanceof Array + && request.batch.operations.length === 1) { + xhr = request.batch.operations[0].xhr; + status = request.batch.operations[0].xhr.status; + } else if (request.jsrecords) { + xhr = request.xhr; + status = request.xhr.status; + } + exception = new Error("Error while saving changes."); + options.error(xhr, status, exception); + } + }; + + if (this.autoSave) { + this.jsdo.saveChanges(this.jsdo._hasSubmitOperation).done(callback).fail(callback); + } else { + this._processChanges(options); + } + }, + create: function (options) { + var jsdo = this.jsdo, + jsrecord, + saveUseRelationships = jsdo.useRelationships; + + options.batch = options.data.models instanceof Array; + try { + jsdo.useRelationships = false; + if (options.batch) { + options.error( + null, + null, + new Error("A newer version of Kendo UI is expected for batching support.") + ); + } else { + jsrecord = jsdo[this.tableRef].add(options.data); + options.data._id = jsrecord.data._id; + this._saveChanges(options); + } + } catch (e) { + // Undo changes on thrown exception + if (jsdo.autoApplyChanges) { + jsdo[this.tableRef].rejectChanges(); + } + options.error(null, null, e); + } finally { + jsdo.useRelationships = saveUseRelationships; + } + }, + update: function (options) { + var jsdo = this.jsdo, + jsrecord, + saveUseRelationships = jsdo.useRelationships; + + options.batch = options.data.models instanceof Array; + try { + jsdo.useRelationships = false; + if (options.batch) { + options.error( + null, + null, + new Error("A newer version of Kendo UI is expected for batching support.") + ); + } else { + jsrecord = jsdo[this.tableRef].findById(options.data._id); + jsrecord.assign(options.data); + this._saveChanges(options); + } + } catch (e) { + // Undo changes on thrown exception + if (jsdo.autoApplyChanges) { + jsdo[this.tableRef].rejectChanges(); + } + options.error(null, null, e); + } finally { + jsdo.useRelationships = saveUseRelationships; + } + }, + destroy: function (options) { + var jsdo = this.jsdo, + jsrecord, + saveUseRelationships = jsdo.useRelationships; + + options.batch = options.data.models instanceof Array; + try { + jsdo.useRelationships = false; + if (options.data.models instanceof Array) { + options.error( + null, + null, + new Error("A newer version of Kendo UI is expected for batching support.") + ); + } else { + jsrecord = jsdo[this.tableRef].findById(options.data._id); + jsrecord.remove(); + this._saveChanges(options); + } + } catch (e) { + // Undo changes on thrown exception + if (jsdo.autoApplyChanges) { + jsdo[this.tableRef].rejectChanges(); + } + options.error(null, null, e); + } finally { + jsdo.useRelationships = saveUseRelationships; + } + }, + submit: function (options) { + var jsdo = this.jsdo, + i, + jsrecord, + saveUseRelationships = jsdo.useRelationships; + + options.batch = true; + try { + jsdo.useRelationships = false; + + if (options.data.created instanceof Array) { + for (i = 0; i < options.data.created.length; i += 1) { + jsrecord = jsdo[this.tableRef].add(options.data.created[i]); + options.data.created[i]._id = jsrecord.data._id; + } + } + + if (options.data.updated instanceof Array) { + for (i = 0; i < options.data.updated.length; i += 1) { + jsrecord = jsdo[this.tableRef].findById(options.data.updated[i]._id); + if (jsrecord) { + jsrecord.assign(options.data.updated[i]); + } else { + options.error(null, null, new Error("Record not found in memory.")); + } + } + } + + if (options.data.destroyed instanceof Array) { + for (i = 0; i < options.data.destroyed.length; i += 1) { + jsrecord = jsdo[this.tableRef].findById(options.data.destroyed[i]._id); + if (jsrecord) { + jsrecord.remove(); + } else { + options.error(null, null, new Error("Record not found in memory.")); + } + } + } + + this._saveChanges(options); + } catch (e) { + // Undo changes on thrown exception + if (jsdo.autoApplyChanges) { + jsdo[this.tableRef].rejectChanges(); + } + options.error(null, null, e); + } finally { + jsdo.useRelationships = saveUseRelationships; + } + } + }); + + // This defines the JSDO DataSource by specifying the schema, transport and reader for it. + // The "id" property is set to "_id" to enable CUD operations. + jQuery.extend(true, kendo.data, { + schemas: { + jsdo: { + type: "jsdo", + model: { + id: "_id" + } + } + }, + transports: { + jsdo: JSDOTransport + }, + readers: { + jsdo: JSDODataReader + } + }); +}()); +/*jslint nomen: false*/ From 5fa8683371da6e6d5044ea1a49d8281f4a569198 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 13 Sep 2023 21:45:56 +0530 Subject: [PATCH 073/135] webui script changes --- webui/build.sh | 5 +++++ webui/src/index.html | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 webui/build.sh diff --git a/webui/build.sh b/webui/build.sh new file mode 100644 index 0000000..fdd51df --- /dev/null +++ b/webui/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "Building Docker Image" +docker build -t webui:latest . --no-cache +echo "Building Docker Image complete!" \ No newline at end of file diff --git a/webui/src/index.html b/webui/src/index.html index 7eee979..ef21b4b 100644 --- a/webui/src/index.html +++ b/webui/src/index.html @@ -44,7 +44,7 @@
-

Welcome to PUG Challenge 2023!

+

Welcome to this awesome PUG Challenge 2023!

From a19b16038a3e3ee8c7199b96935ebb7239bc7c21 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 13 Sep 2023 21:48:04 +0530 Subject: [PATCH 074/135] dockerfile for sports --- Sports/build.config | 2 +- Sports/docker/Dockerfile | 16 ++++++++++++++++ Sports/docker/build.sh | 5 +++++ Sports/docker/test.sh | 10 ++++++++++ Sports/docker/tests/goss.yaml | 16 ++++++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 Sports/docker/Dockerfile create mode 100644 Sports/docker/build.sh create mode 100644 Sports/docker/test.sh create mode 100644 Sports/docker/tests/goss.yaml diff --git a/Sports/build.config b/Sports/build.config index 9420a99..ad42272 100644 --- a/Sports/build.config +++ b/Sports/build.config @@ -120,7 +120,7 @@ avm { // ============================================================================================== // Boolean value to use '_progres' or 'prowin' executables. 'true' for _progres, 'false' for prowin. // ============================================================================================== - tty.enabled="false" + tty.enabled="true" // ============================================================================================== // String value to pass startup parameters. This will append to default params if diff --git a/Sports/docker/Dockerfile b/Sports/docker/Dockerfile new file mode 100644 index 0000000..de4ffab --- /dev/null +++ b/Sports/docker/Dockerfile @@ -0,0 +1,16 @@ +FROM alpine:3.8 + +# Root location +ARG ROOT_FOLDER=/deploy-staging +ARG MANIFEST_VERSION=1.0 + +# Copy archive file +COPY ./ablapps/ ${ROOT_FOLDER}/artifacts/ablapps + +# Create "META-INF/MANIFEST.MF" file +RUN mkdir -p ${ROOT_FOLDER}/META-INF +RUN echo "Manifest-Version: ${MANIFEST_VERSION}" > ${ROOT_FOLDER}/META-INF/MANIFEST.MF +RUN echo "Date-Timestamp: `date +'%Y-%m-%dT%H:%M:%S%z'`" >> ${ROOT_FOLDER}/META-INF/MANIFEST.MF + +# Set working directory +WORKDIR ${ROOT_FOLDER} diff --git a/Sports/docker/build.sh b/Sports/docker/build.sh new file mode 100644 index 0000000..150428b --- /dev/null +++ b/Sports/docker/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "Building Docker Image" +docker build -t ${IMAGE_NAME}:latest . --no-cache +echo "Building Docker Image Complete!" \ No newline at end of file diff --git a/Sports/docker/test.sh b/Sports/docker/test.sh new file mode 100644 index 0000000..ca2e5cb --- /dev/null +++ b/Sports/docker/test.sh @@ -0,0 +1,10 @@ +cd ./tests + +# downlad goss and dgoss +# export GOSS_DST=. +# export GOSS_VER=v0.3.16 +# curl -fsSL https://goss.rocks/install | sh + +export GOSS_PATH=./goss +export GOSS_OPTS="--format junit" +./dgoss run -it sports:latest /bin/sh > unitTestReport.xml \ No newline at end of file diff --git a/Sports/docker/tests/goss.yaml b/Sports/docker/tests/goss.yaml new file mode 100644 index 0000000..07d97e3 --- /dev/null +++ b/Sports/docker/tests/goss.yaml @@ -0,0 +1,16 @@ +file: + /deploy-staging: + exists: true + mode: "0755" + filetype: directory + contains: [] + /deploy-staging/META-INF/MANIFEST.MF: + exists: true + mode: "0644" + filetype: file + contains: [] + /deploy-staging/artifacts/ablapps/Sports.oear: + exists: true + mode: "0777" + filetype: file + contains: [] From aa8f7937c05361cfcee976e27422837e303429dd Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 13 Sep 2023 21:50:11 +0530 Subject: [PATCH 075/135] deploy script changes - use webui as docker image --- deploy/deploy.sh | 9 +++ deploy/docker-compose.yml | 61 +++++++++++++++++++++ deploy/license/.gitignore | 4 ++ deploy/scripts/startServer.sh | 100 ++++++++++++++++++++++++++++++++++ deploy/undeploy.sh | 6 ++ 5 files changed, 180 insertions(+) create mode 100644 deploy/deploy.sh create mode 100644 deploy/docker-compose.yml create mode 100644 deploy/license/.gitignore create mode 100644 deploy/scripts/startServer.sh create mode 100644 deploy/undeploy.sh diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100644 index 0000000..580996c --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# create the app docker image +# docker build --no-cache -t sports:latest . + +# deploy +PAS_INSTANCE_NAME=oepas1 +docker-compose -p ${PAS_INSTANCE_NAME} up -d +echo "PASOE instance named '${PAS_INSTANCE_NAME}_dc' will be available at 'https://localhost:8811'" \ No newline at end of file diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..73afbb7 --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,61 @@ +version: "3.6" +services: + oedbmachine: + image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-db:12.8.0 + ports: + - "7654:7654" + - "7664-7684:7664-7684" + environment: + - DB_BROKER_PORT=7654 + - DB_MINPORT=7664 + - DB_MAXPORT=7684 + webuiapp: + image: webui:latest + volumes: + - webui_dc:/webui + web: + image: nginx + ports: + - "8080:80" + volumes: + - webui_dc:/usr/share/nginx/html:ro + jdk: + image: eclipse-temurin:17.0.3_7-jdk-centos7 + volumes: + - jdk_dc:/opt/java/openjdk + ablapp: + image: sports:latest + volumes: + - app_dc:/deploy-staging/artifacts + pasoeinstance: + image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0 + depends_on: + - jdk + - ablapp + environment: + - FLUENTBIT_LOGGING=true + - APP_NAME=sports + - INSTANCE_NAME=oepas1 + ports: + - "8811:8811" + container_name: "oepas1_pasoeinstance_dc" + command: ["/bin/sh", "-c", "sh /deploy/scripts/startServer.sh"] + volumes: + - type: volume + source: jdk_dc + target: /usr/java + volume: + nocopy: true + - type: volume + source: app_dc + target: /deploy/artifacts + volume: + nocopy: true + - ./license/progress.cfg:/psc/dlc/progress.cfg + # - ./conf/runtime.properties:/deploy/scripts/config/runtime.properties + # - ./conf/logging:/fluentbit-tlr + - ./scripts/startServer.sh:/deploy/scripts/startServer.sh +volumes: + jdk_dc: + app_dc: + webui_dc: diff --git a/deploy/license/.gitignore b/deploy/license/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/deploy/license/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/deploy/scripts/startServer.sh b/deploy/scripts/startServer.sh new file mode 100644 index 0000000..000bfcb --- /dev/null +++ b/deploy/scripts/startServer.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +# Start-up script for PAS for OpenEdge (PASOE) Docker container. +# Creates, deploys and starts the PASOE server. +# Starts the FluentBit process, if 'FLUENTBIT_LOGGING=true'. + +# ------------------------------------------------------------------------------------------------ +# Environment variables provided with the Docker image: +# DLC Path to PAS for OpenEdge installation (/psc/dlc) +# +# WRKDIR Path to the Working directory (/psc/wrk) +# +# JAVA_HOME Path to Java Development Kit (JDK) installation (/usr/java) +# +# Environment variables to be provided: +# INSTANCE_NAME PAS for OpenEdge Instance name +# +# APP_NAME (Optional) Aplication name, will be used if FluentBit logging is be enabled. +# This will get logged as a new field in each log entry +# +# FLUENTBIT_LOGGING (Optional) If set to the string 'true', FluentBit logging will be enabled +# Example: FLUENTBIT_LOGGING="true" +# ------------------------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------------------------ +# Artifacts to be provided: +# License Should be provided at DLC location +# (progress.cfg) +# +# Java Development Should be installed at JAVA_HOME location +# Kit (JDK) +# +# .zip Should be provided at '/deploy/artifacts' +# This is the unit of deployment. Refer to the documentation for more details +# +# runtime.properties (Optional) Should be provided at '/deploy/scripts/config' location. +# This can be used to provide runtime configurations. +# Example: .DB.CONNECTION.PARAMS=-db ... +# ------------------------------------------------------------------------------------------------ + +set -e + +## Validations +# Validate if DLC is set +if [ -z ${DLC} ]; then echo "DLC is not set"; exit 1; fi + +# Validate if WRKDIR is set +if [ -z ${WRKDIR} ]; then echo "WRKDIR is not set"; exit 1; fi + +# Validate if license is provided +if [ ! -s ${DLC}/progress.cfg ]; then echo "License is not provided at ${DLC}"; exit 1; fi + +# Validate if JAVA is present +if [ -z ${JAVA_HOME} ]; then echo "JAVA_HOME is not set"; exit 1; +elif [ ! -x "${JAVA_HOME}/bin/java" ]; then echo "JAVA not found at ${JAVA_HOME}"; exit 1; fi + +# Validate if INSTANCE_NAME is set +if [ -z ${INSTANCE_NAME} ]; then echo "INSTANCE_NAME is not set"; exit 1; fi + +# Validate if ABL App archive is provided +if [ ! -s "/deploy/artifacts/ablapps/Sports.oear" ]; then echo "'Sports.oear' is not provided at '/deploy/artifacts/ablapps'"; exit 1; fi + +## Create directory for additional log files +LOG_PATH=/psc/wrk/logs +mkdir -p ${LOG_PATH} + +## Start PASOE server +cd ${WRKDIR} +${DLC}/bin/pasman create -v -Z pas -u admin:admin ${INSTANCE_NAME} +${INSTANCE_NAME}/bin/tcman.sh import -v /deploy/artifacts/ablapps/Sports.oear +${INSTANCE_NAME}/bin/tcman.sh start + +cd /deploy/scripts +# ant -f ./build.xml -DINSTANCE.ARCHIVE.FILE.ROOT=../artifacts/ deployAndStartInstance -l ${LOG_PATH}/start-server.log + +# SIGTERM is issued by Docker commands (docker stop or docker-compose down) +# Adding a trap for the SIGTERM signal. i.e., to stop PASOE before stopping container +# not_done is a temporary flag +trap 'rm /tmp/not_done; ant -f ./build.xml stopInstance; sleep 10' SIGTERM + +wait_for_sigterm() { + # Loop until not_done flag is removed + touch /tmp/not_done + while [ -f /tmp/not_done ] + do + sleep 60 & + wait $! # This is required such that a trap on main process is triggered + done +} + +## Start FluentBit, if 'FLUENTBIT_LOGGING=true' +if [ "${FLUENTBIT_LOGGING}" = "true" ] +then + export FLUENT_CONFIG_PATH=/etc/fluent-bit + sh /deploy/scripts/setup-fluentbit.sh + /usr/local/bin/fluent-bit -c ${FLUENT_CONFIG_PATH}/fluent-bit.conf -l ${LOG_PATH}/fluent-bit.log & + wait_for_sigterm +else + wait_for_sigterm +fi \ No newline at end of file diff --git a/deploy/undeploy.sh b/deploy/undeploy.sh new file mode 100644 index 0000000..4136638 --- /dev/null +++ b/deploy/undeploy.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# undeploy +PAS_INSTANCE_NAME=oepas1 +docker-compose -p ${PAS_INSTANCE_NAME} down -v +echo "Removed '${PAS_INSTANCE_NAME}_dc'" \ No newline at end of file From 9fc9ead9fb7754c099c6de2262e409c5a47d2be1 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 13 Sep 2023 21:56:57 +0530 Subject: [PATCH 076/135] sports app gradle scripts --- Sports/build.gradle | 143 +++++++++++ Sports/docker/ablapps/.gitignore | 4 + Sports/docker/build.sh | 2 +- Sports/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59536 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + Sports/gradlew | 234 ++++++++++++++++++ Sports/gradlew.bat | 89 +++++++ Sports/settings.gradle | 10 + 8 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 Sports/build.gradle create mode 100644 Sports/docker/ablapps/.gitignore create mode 100644 Sports/gradle/wrapper/gradle-wrapper.jar create mode 100644 Sports/gradle/wrapper/gradle-wrapper.properties create mode 100644 Sports/gradlew create mode 100644 Sports/gradlew.bat create mode 100644 Sports/settings.gradle diff --git a/Sports/build.gradle b/Sports/build.gradle new file mode 100644 index 0000000..1e69251 --- /dev/null +++ b/Sports/build.gradle @@ -0,0 +1,143 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This is a general purpose Gradle build. + * Learn more about Gradle by exploring our samples at https://docs.gradle.org/7.3.3/samples + */ +plugins { + id "progress.openedge.abl" version "2.2.1" +} + +// Gather required variables +def stageEnv = System.getProperty("STAGE_ENVIRONMENT") +stageEnv = stageEnv != null ? stageEnv : System.getenv("STAGE_ENVIRONMENT") +def STAGE_ENVIRONMENT = stageEnv != null ? stageEnv : "dev" + +println "DLC: ${abl.dlcHome}" +println "Stage Environment: ${STAGE_ENVIRONMENT}" + +group = 'com.progress.openedge' +version = "1.0.0" +description = 'Sports App' + +print "Name ${name}" + +abl { + if (STAGE_ENVIRONMENT == "dev") { + dbConnection { + dbName="${buildDir}/db/sports2020/sports" + connectionParameters = "-1" + } + } else { + dbConnection { + parameterFile='conf/startup.pf' + } + } +} + +// ABL App tasks +task createSports2020(type: CreateDB){ + dbName = 'sports' + sourceDb = "${dlcHome}/sports2020" + outputDir = "${buildDir}/db/sports2020" +} +if (STAGE_ENVIRONMENT == "dev") { + compileAbl.dependsOn "createSports2020" +} + +tasks.named("compileAbl-root-AppServer"){ + rcodeDir = "${buildDir}/rcode/AppServer" +} + +tasks.named("compileAbl-root-PASOEContent-WEB-INF-openedge"){ + rcodeDir = "${buildDir}/rcode/PASOEContent/WEB-INF/openedge" +} + +tasks.named("compileAbl-root-tests-AppServer"){ + rcodeDir = "${buildDir}/rcode/tests/AppServer" +} + +task testABLApp(type: ABLUnit){ + source("tests/AppServer") + include("**/*Suite.cls") + propath("tests/AppServer", "AppServer") + outputDir = "${buildDir}/test-results/test" + arguments = [haltOnFailure: "true"] +} +if (STAGE_ENVIRONMENT == "dev") { + testABLApp.dependsOn "createSports2020" +} +check.dependsOn "testABLApp" + +task packageWebApp(type: OEWar){ + webAppName = "Sports" + archiveVersion = "" // to ignore version in the archive name, otherwise has to be handled during deployment + verbose = true + projectLocation = project.projectDir + println "projectLocation: ${projectLocation.get()}" + destinationDirectory = project.file "${project.distsDirectory.get()}/webapps" + + // exclude Sources from 'openedge' directory + // (the ANT task used internally does it by default but added here anyway) + exclude "PASOEContent/WEB-INF/openedge/*.(cls|p|i)" + + webInf { + from("${buildDir}/rcode/PASOEContent/WEB-INF/openedge") + into("openedge") + } + + manifest { + attributes "Implementation-Title": "My Sports ABL Web Application" + attributes "Implementation-Version": "1.0.0" + // from ("PASOEContent/META-INF/MANIFEST.MF") + } +} +packageWebApp.dependsOn "compileAbl-root-PASOEContent-WEB-INF-openedge" + +task packageABLApp(type: Oear) { + ablAppName = "Sports" + archiveVersion = "" + destinationDirectory = project.file "${project.distsDirectory.get()}/ablapps" //will create 'Sports.oear' file at this location + tlr { + from 'tlr' + include '**/*.properties' + include '**/*.xml' + } + webapps { + from "${project.distsDirectory.get()}/webapps" + include '**/*.war' + include '**/*.zip' + } + openedge { + from "${buildDir}/rcode/AppServer" + include '**/*.r' + } + conf { + from 'conf' + exclude '**/*.MF' //exclude direct copy of manifest file and append using manifest section + } + manifest { + attributes "Implementation-Title": "My Sports ABL Application" + attributes "Implementation-Version": "1.0.0" + // from ("conf/MANIFEST.MF") + } +} +packageABLApp.dependsOn "compileAbl-root-AppServer" +packageABLApp.dependsOn "packageWebApp" +assemble.dependsOn "packageABLApp" + +// task dockerBuild(type:Exec) { +// workingDir "${buildDir}/docker" +// commandLine 'bash', '-c', "export IMAGE_NAME=${project.name.toLowerCase()} && sh build.sh" +// doFirst { +// copy { +// from "${project.distsDirectory.get()}/ablapps/" +// into "${buildDir}/docker/ablapps" +// } +// copy { +// from "docker" +// into "${buildDir}/docker" +// } +// } +// } +// dockerBuild.dependsOn build \ No newline at end of file diff --git a/Sports/docker/ablapps/.gitignore b/Sports/docker/ablapps/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/Sports/docker/ablapps/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/Sports/docker/build.sh b/Sports/docker/build.sh index 150428b..4da1d74 100644 --- a/Sports/docker/build.sh +++ b/Sports/docker/build.sh @@ -1,5 +1,5 @@ #!/bin/bash echo "Building Docker Image" -docker build -t ${IMAGE_NAME}:latest . --no-cache +docker build -t sports:latest . --no-cache echo "Building Docker Image Complete!" \ No newline at end of file diff --git a/Sports/gradle/wrapper/gradle-wrapper.jar b/Sports/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7454180f2ae8848c63b8b4dea2cb829da983f2fa GIT binary patch literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL literal 0 HcmV?d00001 diff --git a/Sports/gradle/wrapper/gradle-wrapper.properties b/Sports/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2e6e589 --- /dev/null +++ b/Sports/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Sports/gradlew b/Sports/gradlew new file mode 100644 index 0000000..c53aefa --- /dev/null +++ b/Sports/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions $var, ${var}, ${var:-default}, ${var+SET}, +# ${var#prefix}, ${var%suffix}, and $( cmd ); +# * compound commands having a testable exit status, especially case; +# * various built-in commands including command, set, and ulimit. +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Sports/gradlew.bat b/Sports/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/Sports/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Sports/settings.gradle b/Sports/settings.gradle new file mode 100644 index 0000000..ea406c3 --- /dev/null +++ b/Sports/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.3.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'Sports' From abb317438d79a31c8fded0911ff7f0d24a304562 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 13 Sep 2023 22:02:07 +0530 Subject: [PATCH 077/135] goss for webui --- webui/test.sh | 10 ++++++++++ webui/tests/goss.yaml | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 webui/test.sh create mode 100644 webui/tests/goss.yaml diff --git a/webui/test.sh b/webui/test.sh new file mode 100644 index 0000000..ac5361e --- /dev/null +++ b/webui/test.sh @@ -0,0 +1,10 @@ +cd ./tests + +# downlad goss and dgoss +# export GOSS_DST=. +# export GOSS_VER=v0.3.16 +# curl -fsSL https://goss.rocks/install | sh + +export GOSS_PATH=./goss +export GOSS_OPTS="--format junit" +./dgoss run -it webui:latest /bin/sh > unitTestReport.xml \ No newline at end of file diff --git a/webui/tests/goss.yaml b/webui/tests/goss.yaml new file mode 100644 index 0000000..b804183 --- /dev/null +++ b/webui/tests/goss.yaml @@ -0,0 +1,26 @@ +file: + /webui: + exists: true + mode: "0755" + filetype: directory + contains: [] + /webui/META-INF/MANIFEST.MF: + exists: true + mode: "0644" + filetype: file + contains: [] + /webui/grid.js: + exists: true + mode: "0777" + filetype: file + contains: [] + /webui/index.html: + exists: true + mode: "0777" + filetype: file + contains: [] + /webui/progress.all.js: + exists: true + mode: "0777" + filetype: file + contains: [] From 1024f2ab2d5b6a09b4f62bf56f2d7b616c9b2b65 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 01:41:05 +0530 Subject: [PATCH 078/135] goss command fix --- Sports/docker/test.sh | 4 ++-- webui/test.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sports/docker/test.sh b/Sports/docker/test.sh index ca2e5cb..6ad2132 100644 --- a/Sports/docker/test.sh +++ b/Sports/docker/test.sh @@ -5,6 +5,6 @@ cd ./tests # export GOSS_VER=v0.3.16 # curl -fsSL https://goss.rocks/install | sh -export GOSS_PATH=./goss +# export GOSS_PATH=./goss export GOSS_OPTS="--format junit" -./dgoss run -it sports:latest /bin/sh > unitTestReport.xml \ No newline at end of file +dgoss run -it sports:latest /bin/sh > unitTestReport.xml \ No newline at end of file diff --git a/webui/test.sh b/webui/test.sh index ac5361e..168706e 100644 --- a/webui/test.sh +++ b/webui/test.sh @@ -5,6 +5,6 @@ cd ./tests # export GOSS_VER=v0.3.16 # curl -fsSL https://goss.rocks/install | sh -export GOSS_PATH=./goss +# export GOSS_PATH=./goss export GOSS_OPTS="--format junit" -./dgoss run -it webui:latest /bin/sh > unitTestReport.xml \ No newline at end of file +dgoss run -it webui:latest /bin/sh > unitTestReport.xml \ No newline at end of file From 33fe6a26780bb0ba7032f5ae73e8c5d8f912e0da Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 01:45:10 +0530 Subject: [PATCH 079/135] new flow for build sports app --- .github/workflows/development.yml | 100 +++++++++++++++++++----------- 1 file changed, 63 insertions(+), 37 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index d6a3c80..3b76f48 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -3,22 +3,48 @@ run-name: ${{ github.actor }} is compiling our Sample App 🚀 on: [push] jobs: compile: - name: OpenEdge Compile job + name: OpenEdge Compile Job runs-on: self-hosted + defaults: + run: + working-directory: ./Sports steps: - - name: Clean-up - run: rm -Rf * - uses: actions/checkout@v3 - - run: sh gradlew clean build - working-directory: ./SportsApp - - uses: actions/upload-artifact@v2 # upload test results - if: success() || failure() # run this step even if previous step failed + - name: Running Gradle build + run: sh gradlew clean build + - name: Setup Python # needed by publish step + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Publish ABL Unit Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + with: + files: ./build/test-results/**/*.xml + # - uses: actions/upload-artifact@v2 # upload test results + # if: success() || failure() # run this step even if previous step failed + # with: + # name: test-results + # path: ./build/test-results/test/results.xml + # - name: upload zip file to nexus + # run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz + # working-directory: ./SportsApp + build: + name: Build Docker Image for Sports App + needs: compile + defaults: + run: + working-directory: ./Sports/docker + steps: + - name: Copy ABLApp archive ('.oear') + run: cp -rf ./../build/distributions/ablapps/*.oear ./ablapps + - name: Docker build + run: sh build.sh + - name: Test Docker Image state - Goss + run: sh test.sh + - name: Publish Goss Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 with: - name: test-results - path: ./SportsApp/Sports/build/test-results/test/results.xml - - name: upload zip file to nexus - run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz - working-directory: ./SportsApp + files: ./unitTestReport.xml deploy: name: Sample App Deploy needs: compile @@ -53,31 +79,31 @@ jobs: uses: github/codeql-action/upload-sarif@v2 with: sarif_file: 'trivy-results.sarif' - report: - name: Publish Reports - permissions: write-all - needs: compile - if: always() - runs-on: self-hosted - steps: - - name: Retrieve saved test report - uses: actions/download-artifact@v2 - with: - name: test-results - path: test-results/test - # - uses: dorny/test-reporter@v1.6.0 - # with: - # name: ABL Unit Test Results # Name of the check run which will be created - # path: test-results/test/results.xml # Path to test results - # reporter: java-junit # Format of test results - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action/composite@v2 - with: - files: test-results/**/*.xml + # report: + # name: Publish Reports + # permissions: write-all + # needs: compile + # if: always() + # runs-on: self-hosted + # steps: + # - name: Retrieve saved test report + # uses: actions/download-artifact@v2 + # with: + # name: test-results + # path: test-results/test + # # - uses: dorny/test-reporter@v1.6.0 + # # with: + # # name: ABL Unit Test Results # Name of the check run which will be created + # # path: test-results/test/results.xml # Path to test results + # # reporter: java-junit # Format of test results + # - name: Setup Python + # uses: actions/setup-python@v4 + # with: + # python-version: 3.8 + # - name: Publish Test Results + # uses: EnricoMi/publish-unit-test-result-action/composite@v2 + # with: + # files: test-results/**/*.xml # test: # name: Test deploy of Sample App # needs: deploy From 5ba13151b71670f8f232af4af6c79bc2f24d7c5c Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 01:47:44 +0530 Subject: [PATCH 080/135] fix workflow syntax --- .github/workflows/development.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 3b76f48..8cc7fbf 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -4,6 +4,7 @@ on: [push] jobs: compile: name: OpenEdge Compile Job + permissions: write-all runs-on: self-hosted defaults: run: @@ -31,6 +32,8 @@ jobs: build: name: Build Docker Image for Sports App needs: compile + permissions: write-all + runs-on: self-hosted defaults: run: working-directory: ./Sports/docker From 7770c2cd84d7401b1837570bc76a479bc7df1d24 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 01:58:46 +0530 Subject: [PATCH 081/135] fix build errors --- .github/workflows/development.yml | 4 ++-- Sports/docker/test.sh | 4 +++- webui/test.sh | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 8cc7fbf..49cd5e6 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -20,7 +20,7 @@ jobs: - name: Publish ABL Unit Test Results uses: EnricoMi/publish-unit-test-result-action/composite@v2 with: - files: ./build/test-results/**/*.xml + files: build/test-results/test/results.xml # - uses: actions/upload-artifact@v2 # upload test results # if: success() || failure() # run this step even if previous step failed # with: @@ -47,7 +47,7 @@ jobs: - name: Publish Goss Test Results uses: EnricoMi/publish-unit-test-result-action/composite@v2 with: - files: ./unitTestReport.xml + files: tests/unitTestReport.xml deploy: name: Sample App Deploy needs: compile diff --git a/Sports/docker/test.sh b/Sports/docker/test.sh index 6ad2132..1984082 100644 --- a/Sports/docker/test.sh +++ b/Sports/docker/test.sh @@ -7,4 +7,6 @@ cd ./tests # export GOSS_PATH=./goss export GOSS_OPTS="--format junit" -dgoss run -it sports:latest /bin/sh > unitTestReport.xml \ No newline at end of file +echo "Running Goss Tests" +dgoss run -it sports:latest /bin/sh > unitTestReport.xml +echo "Running Goss Tests Complete" \ No newline at end of file diff --git a/webui/test.sh b/webui/test.sh index 168706e..506155e 100644 --- a/webui/test.sh +++ b/webui/test.sh @@ -7,4 +7,6 @@ cd ./tests # export GOSS_PATH=./goss export GOSS_OPTS="--format junit" -dgoss run -it webui:latest /bin/sh > unitTestReport.xml \ No newline at end of file +echo "Running Goss Tests" +dgoss run -it webui:latest /bin/sh > unitTestReport.xml +echo "Running Goss Tests Complete" \ No newline at end of file From 6f73d0e69dc74c50d409a0ff62c77ae1a8af9065 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 02:06:41 +0530 Subject: [PATCH 082/135] debug --- .github/workflows/development.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 49cd5e6..adbdf1e 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -19,8 +19,9 @@ jobs: python-version: 3.8 - name: Publish ABL Unit Test Results uses: EnricoMi/publish-unit-test-result-action/composite@v2 + if: always() with: - files: build/test-results/test/results.xml + files: Sports/docker/build/test-results/test/results.xml # - uses: actions/upload-artifact@v2 # upload test results # if: success() || failure() # run this step even if previous step failed # with: From 27bc040f976e32ec87e5beb2d06c5a050dfdf752 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 02:12:25 +0530 Subject: [PATCH 083/135] debug --- .github/workflows/development.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index adbdf1e..3ef592a 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v3 - name: Running Gradle build run: sh gradlew clean build - - name: Setup Python # needed by publish step + - name: Setup Python - needed by publish step uses: actions/setup-python@v4 with: python-version: 3.8 @@ -21,7 +21,7 @@ jobs: uses: EnricoMi/publish-unit-test-result-action/composite@v2 if: always() with: - files: Sports/docker/build/test-results/test/results.xml + files: /opt/actions-runner/_work/OpenEdgeOps/OpenEdgeOps/Sports/docker/build/test-results/test/results.xml # - uses: actions/upload-artifact@v2 # upload test results # if: success() || failure() # run this step even if previous step failed # with: From d859ae197c99803840afb4b8f4c7c9eeaccd5ff9 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 02:14:27 +0530 Subject: [PATCH 084/135] debug --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 3ef592a..a4ebc78 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -21,7 +21,7 @@ jobs: uses: EnricoMi/publish-unit-test-result-action/composite@v2 if: always() with: - files: /opt/actions-runner/_work/OpenEdgeOps/OpenEdgeOps/Sports/docker/build/test-results/test/results.xml + files: /opt/actions-runner/_work/OpenEdgeOps/OpenEdgeOps/Sports/build/test-results/test/results.xml # - uses: actions/upload-artifact@v2 # upload test results # if: success() || failure() # run this step even if previous step failed # with: From 9c3c89fbba2cafd5987086f71aec21478542fd45 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 02:20:03 +0530 Subject: [PATCH 085/135] fix report publish issues --- .github/workflows/development.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index a4ebc78..827dd8b 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -8,7 +8,7 @@ jobs: runs-on: self-hosted defaults: run: - working-directory: ./Sports + working-directory: ${{GITHUB_WORKSPACE}}/Sports steps: - uses: actions/checkout@v3 - name: Running Gradle build @@ -21,7 +21,7 @@ jobs: uses: EnricoMi/publish-unit-test-result-action/composite@v2 if: always() with: - files: /opt/actions-runner/_work/OpenEdgeOps/OpenEdgeOps/Sports/build/test-results/test/results.xml + files: ${{GITHUB_WORKSPACE}}/Sports/build/test-results/test/*.xml # - uses: actions/upload-artifact@v2 # upload test results # if: success() || failure() # run this step even if previous step failed # with: @@ -37,7 +37,7 @@ jobs: runs-on: self-hosted defaults: run: - working-directory: ./Sports/docker + working-directory: ${{GITHUB_WORKSPACE}}/Sports/docker steps: - name: Copy ABLApp archive ('.oear') run: cp -rf ./../build/distributions/ablapps/*.oear ./ablapps @@ -45,10 +45,14 @@ jobs: run: sh build.sh - name: Test Docker Image state - Goss run: sh test.sh + - name: Setup Python - needed by publish step + uses: actions/setup-python@v4 + with: + python-version: 3.8 - name: Publish Goss Test Results uses: EnricoMi/publish-unit-test-result-action/composite@v2 with: - files: tests/unitTestReport.xml + files: ${{GITHUB_WORKSPACE}}/Sports/docker/tests/*.xml deploy: name: Sample App Deploy needs: compile From 6a3904f5563f7d5c1120264b8299867dee4f0d1a Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 02:22:14 +0530 Subject: [PATCH 086/135] trigger build --- .github/workflows/development.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 827dd8b..ef10e36 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -125,4 +125,3 @@ jobs: - From 4a9d2b21c84fd072791608ca14ea1e434f16282c Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 02:28:43 +0530 Subject: [PATCH 087/135] fix workspace var --- .github/workflows/development.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index ef10e36..4259bc6 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -8,7 +8,7 @@ jobs: runs-on: self-hosted defaults: run: - working-directory: ${{GITHUB_WORKSPACE}}/Sports + working-directory: ${{ github.workspace }}/Sports steps: - uses: actions/checkout@v3 - name: Running Gradle build @@ -21,7 +21,7 @@ jobs: uses: EnricoMi/publish-unit-test-result-action/composite@v2 if: always() with: - files: ${{GITHUB_WORKSPACE}}/Sports/build/test-results/test/*.xml + files: ${{ github.workspace }}/Sports/build/test-results/test/*.xml # - uses: actions/upload-artifact@v2 # upload test results # if: success() || failure() # run this step even if previous step failed # with: @@ -37,7 +37,7 @@ jobs: runs-on: self-hosted defaults: run: - working-directory: ${{GITHUB_WORKSPACE}}/Sports/docker + working-directory: ${{ github.workspace }}/Sports/docker steps: - name: Copy ABLApp archive ('.oear') run: cp -rf ./../build/distributions/ablapps/*.oear ./ablapps @@ -52,7 +52,7 @@ jobs: - name: Publish Goss Test Results uses: EnricoMi/publish-unit-test-result-action/composite@v2 with: - files: ${{GITHUB_WORKSPACE}}/Sports/docker/tests/*.xml + files: ${{ github.workspace }}/Sports/docker/tests/*.xml deploy: name: Sample App Deploy needs: compile From 6eaebdbfcd7acb76168a05ae1f705e27c069fc99 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 02:33:25 +0530 Subject: [PATCH 088/135] remove modes from goss yaml --- .github/workflows/development.yml | 2 +- Sports/docker/tests/goss.yaml | 3 --- webui/tests/goss.yaml | 5 ----- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 4259bc6..2ad489f 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -55,7 +55,7 @@ jobs: files: ${{ github.workspace }}/Sports/docker/tests/*.xml deploy: name: Sample App Deploy - needs: compile + needs: build permissions: write-all runs-on: self-hosted defaults: diff --git a/Sports/docker/tests/goss.yaml b/Sports/docker/tests/goss.yaml index 07d97e3..9651cc3 100644 --- a/Sports/docker/tests/goss.yaml +++ b/Sports/docker/tests/goss.yaml @@ -1,16 +1,13 @@ file: /deploy-staging: exists: true - mode: "0755" filetype: directory contains: [] /deploy-staging/META-INF/MANIFEST.MF: exists: true - mode: "0644" filetype: file contains: [] /deploy-staging/artifacts/ablapps/Sports.oear: exists: true - mode: "0777" filetype: file contains: [] diff --git a/webui/tests/goss.yaml b/webui/tests/goss.yaml index b804183..7be8578 100644 --- a/webui/tests/goss.yaml +++ b/webui/tests/goss.yaml @@ -1,26 +1,21 @@ file: /webui: exists: true - mode: "0755" filetype: directory contains: [] /webui/META-INF/MANIFEST.MF: exists: true - mode: "0644" filetype: file contains: [] /webui/grid.js: exists: true - mode: "0777" filetype: file contains: [] /webui/index.html: exists: true - mode: "0777" filetype: file contains: [] /webui/progress.all.js: exists: true - mode: "0777" filetype: file contains: [] From fd4b721adbc1beddbf31c472cb8727ff574cea67 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 02:40:51 +0530 Subject: [PATCH 089/135] take care of exit code --- Sports/docker/test.sh | 1 - webui/test.sh | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Sports/docker/test.sh b/Sports/docker/test.sh index 1984082..b70da92 100644 --- a/Sports/docker/test.sh +++ b/Sports/docker/test.sh @@ -9,4 +9,3 @@ cd ./tests export GOSS_OPTS="--format junit" echo "Running Goss Tests" dgoss run -it sports:latest /bin/sh > unitTestReport.xml -echo "Running Goss Tests Complete" \ No newline at end of file diff --git a/webui/test.sh b/webui/test.sh index 506155e..81da9dc 100644 --- a/webui/test.sh +++ b/webui/test.sh @@ -8,5 +8,4 @@ cd ./tests # export GOSS_PATH=./goss export GOSS_OPTS="--format junit" echo "Running Goss Tests" -dgoss run -it webui:latest /bin/sh > unitTestReport.xml -echo "Running Goss Tests Complete" \ No newline at end of file +dgoss run -it webui:latest /bin/sh > unitTestReport.xml \ No newline at end of file From ab227430a794284d22dc4add7035b183a33ce74d Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 02:47:24 +0530 Subject: [PATCH 090/135] test result name --- .github/workflows/development.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 2ad489f..3ca15c7 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -22,6 +22,7 @@ jobs: if: always() with: files: ${{ github.workspace }}/Sports/build/test-results/test/*.xml + check_name: ABL Unit Test Results for Sports App # - uses: actions/upload-artifact@v2 # upload test results # if: success() || failure() # run this step even if previous step failed # with: @@ -53,6 +54,7 @@ jobs: uses: EnricoMi/publish-unit-test-result-action/composite@v2 with: files: ${{ github.workspace }}/Sports/docker/tests/*.xml + check_name: Goss Test Results for Sports App deploy: name: Sample App Deploy needs: build From 0aaa979a772d3c97ae6b3c6889d0376298e7909f Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 02:57:26 +0530 Subject: [PATCH 091/135] docker build for webui --- .github/workflows/development.yml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 3ca15c7..4e57ebd 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -55,9 +55,30 @@ jobs: with: files: ${{ github.workspace }}/Sports/docker/tests/*.xml check_name: Goss Test Results for Sports App + buildwebui: + name: Build Docker Image for Web UI App + permissions: write-all + runs-on: self-hosted + defaults: + run: + working-directory: ${{ github.workspace }}/webui + steps: + - name: Docker build + run: sh build.sh + - name: Test Docker Image state - Goss + run: sh test.sh + - name: Setup Python - needed by publish step + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Publish Goss Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + with: + files: ${{ github.workspace }}/webui/tests/*.xml + check_name: Goss Test Results for Web UI App deploy: name: Sample App Deploy - needs: build + needs: [build, buildwebui] permissions: write-all runs-on: self-hosted defaults: From 4c0e362e22aa87419d29bad48454dad0bed84184 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 02:59:29 +0530 Subject: [PATCH 092/135] docker build for webui --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 4e57ebd..8343790 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -55,7 +55,7 @@ jobs: with: files: ${{ github.workspace }}/Sports/docker/tests/*.xml check_name: Goss Test Results for Sports App - buildwebui: + buildwebui: name: Build Docker Image for Web UI App permissions: write-all runs-on: self-hosted From 5f18df24e533ddac359f515317ae913dea06203c Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 03:19:33 +0530 Subject: [PATCH 093/135] Push docker images to nexus --- .github/workflows/development.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 8343790..57225ab 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -55,6 +55,11 @@ jobs: with: files: ${{ github.workspace }}/Sports/docker/tests/*.xml check_name: Goss Test Results for Sports App + - name: Push Docker Image to Develop Docker Registry + run: | + docker tag sports:latest ec2-54-80-142-101.compute-1.amazonaws.com:9443/sports:latest + docker login ec2-54-80-142-101.compute-1.amazonaws.com:9443 -u admin -p ${{ secrets.DOCKER_PWD }} + docker push ec2-54-80-142-101.compute-1.amazonaws.com:9443/sports:latest buildwebui: name: Build Docker Image for Web UI App permissions: write-all @@ -76,6 +81,11 @@ jobs: with: files: ${{ github.workspace }}/webui/tests/*.xml check_name: Goss Test Results for Web UI App + - name: Push Docker Image to Develop Docker Registry + run: | + docker tag webui:latest ec2-54-80-142-101.compute-1.amazonaws.com:9443/webui:latest + docker login ec2-54-80-142-101.compute-1.amazonaws.com:9443 -u admin -p ${{ secrets.DOCKER_PWD }} + docker push ec2-54-80-142-101.compute-1.amazonaws.com:9443/webui:latest deploy: name: Sample App Deploy needs: [build, buildwebui] From 31eac80798532f81d718e3f0202ec00d6f50dce6 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 03:29:35 +0530 Subject: [PATCH 094/135] change in deploy scripts --- .github/workflows/development.yml | 50 +++++++++++++++++++++++-------- deploy/docker-compose.yml | 4 +-- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 57225ab..0283fb0 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -93,33 +93,59 @@ jobs: runs-on: self-hosted defaults: run: - working-directory: ${{vars.SAMPLEAPP_DIR}} + working-directory: ${{ github.workspace }}/deploy steps: - - name: Clean-up - run: rm -Rf * - - name: Download the application artifact - run: wget ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz --no-check-certificate - - name: Extract the artifact - run: tar -zxf ${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz - - run: mkdir ./deploy/license + - run: mkdir ./license - name: Download the OpenEdge License file - run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate + run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./license/progress.cfg --no-check-certificate - name: Undeploy previous version of Sample App run: sudo sh undeploy.sh - working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy - name: Deploy new version of Sample App run: sudo sh deploy.sh - working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy - name: Run security scan for Sample App docker image uses: aquasecurity/trivy-action@master with: - image-ref: 'docker.io/sports:latest' + image-ref: 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/sports:latest' format: 'sarif' output: 'trivy-results.sarif' - name: Upload security scan report of Sample App docker image to GitHub Security tab uses: github/codeql-action/upload-sarif@v2 with: sarif_file: 'trivy-results.sarif' + # deploy: + # name: Sample App Deploy + # needs: [build, buildwebui] + # permissions: write-all + # runs-on: self-hosted + # defaults: + # run: + # working-directory: ${{vars.SAMPLEAPP_DIR}} + # steps: + # - name: Clean-up + # run: rm -Rf * + # - name: Download the application artifact + # run: wget ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz --no-check-certificate + # - name: Extract the artifact + # run: tar -zxf ${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz + # - run: mkdir ./deploy/license + # - name: Download the OpenEdge License file + # run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate + # - name: Undeploy previous version of Sample App + # run: sudo sh undeploy.sh + # working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy + # - name: Deploy new version of Sample App + # run: sudo sh deploy.sh + # working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy + # - name: Run security scan for Sample App docker image + # uses: aquasecurity/trivy-action@master + # with: + # image-ref: 'docker.io/sports:latest' + # format: 'sarif' + # output: 'trivy-results.sarif' + # - name: Upload security scan report of Sample App docker image to GitHub Security tab + # uses: github/codeql-action/upload-sarif@v2 + # with: + # sarif_file: 'trivy-results.sarif' # report: # name: Publish Reports # permissions: write-all diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 73afbb7..f423767 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -10,7 +10,7 @@ services: - DB_MINPORT=7664 - DB_MAXPORT=7684 webuiapp: - image: webui:latest + image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/webui:latest volumes: - webui_dc:/webui web: @@ -24,7 +24,7 @@ services: volumes: - jdk_dc:/opt/java/openjdk ablapp: - image: sports:latest + image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/sports:latest volumes: - app_dc:/deploy-staging/artifacts pasoeinstance: From a3aeac7cec5428555e05e1d2b2a5c9b072e92525 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 03:33:03 +0530 Subject: [PATCH 095/135] mkdir fix --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 0283fb0..1de5f82 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -95,7 +95,7 @@ jobs: run: working-directory: ${{ github.workspace }}/deploy steps: - - run: mkdir ./license + - run: mkdir -p ./license - name: Download the OpenEdge License file run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./license/progress.cfg --no-check-certificate - name: Undeploy previous version of Sample App From fec01fa44dc5c225fa24ea3668291bd10a7b6535 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 03:46:42 +0530 Subject: [PATCH 096/135] Security scan job --- .github/workflows/development.yml | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 1de5f82..03280c0 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -87,7 +87,7 @@ jobs: docker login ec2-54-80-142-101.compute-1.amazonaws.com:9443 -u admin -p ${{ secrets.DOCKER_PWD }} docker push ec2-54-80-142-101.compute-1.amazonaws.com:9443/webui:latest deploy: - name: Sample App Deploy + name: Test Sample App Deploy needs: [build, buildwebui] permissions: write-all runs-on: self-hosted @@ -102,16 +102,34 @@ jobs: run: sudo sh undeploy.sh - name: Deploy new version of Sample App run: sudo sh deploy.sh - - name: Run security scan for Sample App docker image + securityscans: + name: Scan Docker Images for Security Vulnerabilities + needs: [build, buildwebui] + permissions: write-all + runs-on: self-hosted + steps: + - name: Run security scan for Sports App docker image uses: aquasecurity/trivy-action@master with: image-ref: 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/sports:latest' format: 'sarif' - output: 'trivy-results.sarif' - - name: Upload security scan report of Sample App docker image to GitHub Security tab + output: 'trivy-results-sports.sarif' + - name: Upload security scan report of Sports App docker image to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: 'trivy-results-sports.sarif' + category: sports-app-scan + - name: Run security scan for Web UI App docker image + uses: aquasecurity/trivy-action@master + with: + image-ref: 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/webui:latest' + format: 'sarif' + output: 'trivy-results-webui.sarif' + - name: Upload security scan report of Web UI App docker image to GitHub Security tab uses: github/codeql-action/upload-sarif@v2 with: - sarif_file: 'trivy-results.sarif' + sarif_file: 'trivy-results-webui.sarif' + category: webui-app-scan # deploy: # name: Sample App Deploy # needs: [build, buildwebui] From a50dfe852535e447118f57d4a40add0c7c0e9498 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 14 Sep 2023 03:54:49 +0530 Subject: [PATCH 097/135] Placer holder job for push to release --- .github/workflows/development.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 03280c0..9941bdd 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -102,6 +102,10 @@ jobs: run: sudo sh undeploy.sh - name: Deploy new version of Sample App run: sudo sh deploy.sh + - name: Test the app + run: echo "TODO" + - name: Undeploy the app and clean up resources + run: echo "TODO" securityscans: name: Scan Docker Images for Security Vulnerabilities needs: [build, buildwebui] @@ -130,6 +134,13 @@ jobs: with: sarif_file: 'trivy-results-webui.sarif' category: webui-app-scan + stage: + name: Stage Artifacts and Docker Images for Release + needs: [deploy, securityscans] + runs-on: self-hosted + steps: + - name: Publish Docker Images to Release Docker Registry + run: echo "TODO" # deploy: # name: Sample App Deploy # needs: [build, buildwebui] From 5adeb499875b5badbd24e8c12ef4b616a84f59fc Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Fri, 15 Sep 2023 04:05:10 +0530 Subject: [PATCH 098/135] add dependency web app container --- deploy/docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index f423767..6651c3a 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -15,6 +15,8 @@ services: - webui_dc:/webui web: image: nginx + depends_on: + - webuiapp ports: - "8080:80" volumes: From 7b7958b38b58a347ef8b178a8fc830f834223ba2 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Mon, 18 Sep 2023 14:30:47 +0530 Subject: [PATCH 099/135] clean up --- SportsApp/.gitignore | 0 SportsApp/Sports/.dbconnection | 4 - SportsApp/Sports/.gitattributes | 6 - SportsApp/Sports/.gitignore | 9 - SportsApp/Sports/.project | 29 - SportsApp/Sports/.propath | 13 - .../Sports/.scripts/common-build-tasks.xml | 140 - SportsApp/Sports/.scripts/deploy-app.sh | 27 - .../.scripts/dotpropath-to-pctpropath.xsl | 41 - SportsApp/Sports/.scripts/propath.xml | 38 - SportsApp/Sports/.scripts/standardpaths.xml | 71 - .../Sports/.services/AppServer/Customer.pidl | 870 - .../Sports/.services/AppServer/Item.pidl | 635 - .../Sports/.services/AppServer/Order.pidl | 670 - .../Sports/.services/AppServer/Salesrep.pidl | 285 - .../Sports/.services/AppServer/State.pidl | 250 - .../rest/SportsService/resourceModel.xml | 63 - .../Expose/rest/SportsService/spring.xml | 78 - SportsApp/Sports/.services/Sports.properties | 4 - SportsApp/Sports/.services/adapters.pamf | 392 - .../org.eclipse.wst.common.component | 3 - ....eclipse.wst.common.project.facet.core.xml | 10 - .../.settings/pasoe.appserver.settings.xml | 6 - SportsApp/Sports/.settings/pasoe.config.xml | 4 - SportsApp/Sports/AppServer/Customer.cls | 253 - SportsApp/Sports/AppServer/GetCustOrders.p | 54 - SportsApp/Sports/AppServer/Item.cls | 118 - SportsApp/Sports/AppServer/Order.cls | 118 - SportsApp/Sports/AppServer/Salesrep.cls | 118 - SportsApp/Sports/AppServer/State.cls | 118 - SportsApp/Sports/AppServer/UpdateCustOrders.p | 53 - SportsApp/Sports/AppServer/customer.i | 27 - SportsApp/Sports/AppServer/item.i | 44 - SportsApp/Sports/AppServer/myCustOrder.i | 34 - SportsApp/Sports/AppServer/order.i | 45 - SportsApp/Sports/AppServer/salesrep.i | 30 - .../AppServer/sports2020trgs/asordnum.p | 12 - .../Sports/AppServer/sports2020trgs/asstate.p | 31 - .../Sports/AppServer/sports2020trgs/crbin.p | 15 - .../Sports/AppServer/sports2020trgs/crcust.p | 15 - .../Sports/AppServer/sports2020trgs/cremp.p | 4 - .../Sports/AppServer/sports2020trgs/crintr.p | 4 - .../Sports/AppServer/sports2020trgs/crinv.p | 14 - .../Sports/AppServer/sports2020trgs/critem.p | 14 - .../AppServer/sports2020trgs/crlocdef.p | 4 - .../Sports/AppServer/sports2020trgs/crord.p | 19 - .../Sports/AppServer/sports2020trgs/crordl.p | 11 - .../Sports/AppServer/sports2020trgs/crpo.p | 4 - .../Sports/AppServer/sports2020trgs/crsuppl.p | 4 - .../Sports/AppServer/sports2020trgs/crware.p | 3 - .../Sports/AppServer/sports2020trgs/delcust.p | 51 - .../Sports/AppServer/sports2020trgs/delinv.p | 19 - .../Sports/AppServer/sports2020trgs/delitem.p | 7 - .../Sports/AppServer/sports2020trgs/delord.p | 29 - .../Sports/AppServer/sports2020trgs/delordl.p | 16 - .../Sports/AppServer/sports2020trgs/delsup.p | 28 - .../AppServer/sports2020trgs/ref_call.p | 13 - .../Sports/AppServer/sports2020trgs/wrcust.p | 52 - .../Sports/AppServer/sports2020trgs/writem.p | 54 - .../Sports/AppServer/sports2020trgs/wrord.p | 11 - .../Sports/AppServer/sports2020trgs/wrordl.p | 32 - SportsApp/Sports/AppServer/state.i | 29 - SportsApp/Sports/Dockerfile | 17 - .../Sports/PASOEContent/META-INF/MANIFEST.MF | 8 - .../PASOEContent/WEB-INF/adapters/rest/README | 120 - .../rest/_oepingService/_oepingService.paar | Bin 2871 -> 0 bytes .../WEB-INF/adapters/rest/runtime.props | 21 - .../PASOEContent/WEB-INF/adapters/soap/README | 71 - .../WEB-INF/adapters/soap/camel-spring.xml | 21 - .../WEB-INF/adapters/web/README.txt | 34 - .../WEB-INF/backup/apsv-basic.xml | 77 - .../PASOEContent/WEB-INF/backup/apsv-none.xml | 14 - .../backup/oeablSecurity-anonymous.xml | 218 - .../backup/oeablSecurity-basic-ldap-ext.xml | 450 - .../backup/oeablSecurity-basic-ldap.xml | 346 - .../backup/oeablSecurity-basic-local.xml | 332 - .../backup/oeablSecurity-basic-oerealm.xml | 411 - .../backup/oeablSecurity-basic-saml.xml | 496 - .../backup/oeablSecurity-container.xml | 305 - .../backup/oeablSecurity-form-ldap-ext.xml | 501 - .../backup/oeablSecurity-form-ldap.xml | 403 - .../backup/oeablSecurity-form-local.xml | 385 - .../backup/oeablSecurity-form-oerealm.xml | 455 - .../backup/oeablSecurity-form-saml.xml | 541 - .../WEB-INF/backup/soap-basic.xml | 43 - .../PASOEContent/WEB-INF/backup/soap-none.xml | 14 - .../PASOEContent/WEB-INF/classes/manifest.txt | 0 .../WEB-INF/home/ServerStatus.html | 132 - .../WEB-INF/jsp/errorJSONBody.jsp | 54 - .../PASOEContent/WEB-INF/jsp/errorPage.jsp | 39 - .../WEB-INF/jsp/errorPageBody.jsp | 54 - .../WEB-INF/jsp/errorPageFooter.jsp | 2 - .../WEB-INF/jsp/errorPageHeader.jsp | 3 - .../WEB-INF/jsp/exceptionPage.jsp | 38 - .../WEB-INF/jsp/exceptionPageFooter.jsp | 2 - .../WEB-INF/jsp/exceptionPageHeader.jsp | 3 - .../WEB-INF/jsp/httpCodeDesc-terse.properties | 25 - .../jsp/httpCodeDesc-verbose.properties | 25 - .../WEB-INF/jsp/loadErrorData.jsp | 241 - .../PASOEContent/WEB-INF/jsp/loginPage.jsp | 37 - .../PASOEContent/WEB-INF/jsp/logoutPage.jsp | 40 - .../Sports/PASOEContent/WEB-INF/logging.xml | 35 - .../PASOEContent/WEB-INF/metadata/README | 13 - .../PASOEContent/WEB-INF/oeablSecurity.csv | 59 - .../WEB-INF/oeablSecurity.properties | 656 - .../WEB-INF/oeablSecurity.properties.README | 25 - .../PASOEContent/WEB-INF/oeablSecurity.xml | 70 - .../PASOEContent/WEB-INF/oeablSecurityJWT.csv | 49 - .../PASOEContent/WEB-INF/openedge/ReadMe.txt | 1 - .../Sports/PASOEContent/WEB-INF/security.tld | 195 - .../WEB-INF/spring/properties-loader.xml | 38 - .../PASOEContent/WEB-INF/tlr/Merge.template | 30 - .../Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr | 0 .../PASOEContent/WEB-INF/users.properties | 4 - SportsApp/Sports/PASOEContent/WEB-INF/web.xml | 585 - .../PASOEContent/WEB-INF/web.xml-clientcert | 583 - SportsApp/Sports/PASOEContent/favicon.ico | Bin 370070 -> 0 bytes SportsApp/Sports/PASOEContent/index.jsp | 32 - .../PASOEContent/static/ServerStatus.html | 132 - .../PASOEContent/static/SportsService.json | 935 - .../PASOEContent/static/auth/login.html | 10 - .../Sports/PASOEContent/static/auth/login.jsp | 9 - .../PASOEContent/static/auth/loginfail.html | 6 - .../PASOEContent/static/auth/logout.html | 9 - .../PASOEContent/static/auth/logout.jsp | 11 - .../PASOEContent/static/commonPageFooter.html | 6 - .../PASOEContent/static/commonPageHeader.html | 14 - .../PASOEContent/static/commonStyle.css | 275 - .../PASOEContent/static/error/error401.html | 3 - .../PASOEContent/static/error/error403.html | 3 - .../PASOEContent/static/error/error404.html | 3 - .../PASOEContent/static/error/error500.html | 3 - .../PASOEContent/static/error/error503.html | 3 - .../Sports/PASOEContent/static/home.html | 5 - SportsApp/Sports/PASOEContent/static/home.jsp | 62 - .../PASOEContent/static/images/Thumbs.db | Bin 108032 -> 0 bytes .../PASOEContent/static/images/appserver.png | Bin 1619 -> 0 bytes .../PASOEContent/static/images/appserver2.png | Bin 1627 -> 0 bytes .../PASOEContent/static/images/background.png | Bin 243375 -> 0 bytes .../static/images/communities.png | Bin 3328 -> 0 bytes .../PASOEContent/static/images/content.png | Bin 2510 -> 0 bytes .../static/images/documentation.png | Bin 1201 -> 0 bytes .../PASOEContent/static/images/gear.png | Bin 17253 -> 0 bytes .../PASOEContent/static/images/progress.png | Bin 15189 -> 0 bytes .../PASOEContent/static/images/qstart.png | Bin 3028 -> 0 bytes .../PASOEContent/static/images/videos.png | Bin 1445 -> 0 bytes .../Sports/PASOEContent/static/index.jsp | 8 - .../PASOEContent/static/serverstatus.js | 78 - .../PASOEContent/static/sessionsExceeded.jsp | 22 - .../Sports/build-output/AppServer/Customer.r | Bin 0 -> 47261 bytes .../build-output/AppServer/GetCustOrders.r | Bin 0 -> 14664 bytes .../Sports/build-output/AppServer/Item.r | Bin 0 -> 24343 bytes .../Sports/build-output/AppServer/Order.r | Bin 0 -> 25383 bytes .../Sports/build-output/AppServer/Salesrep.r | Bin 0 -> 17362 bytes .../Sports/build-output/AppServer/State.r | Bin 0 -> 16295 bytes .../build-output/AppServer/UpdateCustOrders.r | Bin 0 -> 16797 bytes .../AppServer/sports2020trgs/asordnum.r | Bin 0 -> 2005 bytes .../AppServer/sports2020trgs/asstate.r | Bin 0 -> 3161 bytes .../AppServer/sports2020trgs/crbin.r | Bin 0 -> 2053 bytes .../AppServer/sports2020trgs/crcust.r | Bin 0 -> 2319 bytes .../AppServer/sports2020trgs/cremp.r | Bin 0 -> 2295 bytes .../AppServer/sports2020trgs/crintr.r | Bin 0 -> 2211 bytes .../AppServer/sports2020trgs/crinv.r | Bin 0 -> 2125 bytes .../AppServer/sports2020trgs/critem.r | Bin 0 -> 2247 bytes .../AppServer/sports2020trgs/crlocdef.r | Bin 0 -> 2223 bytes .../AppServer/sports2020trgs/crord.r | Bin 0 -> 2409 bytes .../AppServer/sports2020trgs/crordl.r | Bin 0 -> 1775 bytes .../AppServer/sports2020trgs/crpo.r | Bin 0 -> 2129 bytes .../AppServer/sports2020trgs/crsuppl.r | Bin 0 -> 2263 bytes .../AppServer/sports2020trgs/crware.r | Bin 0 -> 2177 bytes .../AppServer/sports2020trgs/delcust.r | Bin 0 -> 6849 bytes .../AppServer/sports2020trgs/delinv.r | Bin 0 -> 2300 bytes .../AppServer/sports2020trgs/delitem.r | Bin 0 -> 3105 bytes .../AppServer/sports2020trgs/delord.r | Bin 0 -> 3352 bytes .../AppServer/sports2020trgs/delordl.r | Bin 0 -> 2071 bytes .../AppServer/sports2020trgs/delsup.r | Bin 0 -> 5560 bytes .../AppServer/sports2020trgs/ref_call.r | Bin 0 -> 2109 bytes .../AppServer/sports2020trgs/wrcust.r | Bin 0 -> 9307 bytes .../AppServer/sports2020trgs/writem.r | Bin 0 -> 6313 bytes .../AppServer/sports2020trgs/wrord.r | Bin 0 -> 1903 bytes .../AppServer/sports2020trgs/wrordl.r | Bin 0 -> 2135 bytes .../tests/AppServer/CustomerTest.r | Bin 0 -> 12461 bytes .../build-output/tests/AppServer/OrderTest.r | Bin 0 -> 4936 bytes .../tests/AppServer/SportsTestSuite.r | Bin 0 -> 2624 bytes SportsApp/Sports/build.config | 228 - SportsApp/Sports/build.gradle | 142 - SportsApp/Sports/conf/MANIFEST.MF | 3 - SportsApp/Sports/conf/startup.pf | 1 - SportsApp/Sports/gradle.properties | 2 - .../Sports/gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - SportsApp/Sports/gradlew | 234 - SportsApp/Sports/gradlew.bat | 89 - SportsApp/Sports/settings.gradle | 10 - .../Sports/tests/AppServer/CustomerTest.cls | 162 - .../Sports/tests/AppServer/OrderTest.cls | 43 - .../tests/AppServer/SportsTestSuite.cls | 16 - SportsApp/Sports/tests/CustomerTest.cls | 162 - SportsApp/Sports/tests/OrderTest.cls | 43 - SportsApp/Sports/tests/SportsTestSuite.cls | 16 - SportsApp/Sports/tlr/merge.properties | 52 - .../Sports/tlr/openedge.properties.merge | 2 - SportsApp/Sports/velocity.log | 420 + SportsApp/build.gradle | 31 - SportsApp/deploy/.gitignore | 2 - SportsApp/deploy/Dockerfile | 17 - SportsApp/deploy/ablapps/.gitignore | 4 - SportsApp/deploy/deploy.sh | 9 - SportsApp/deploy/docker-compose.yml | 56 - SportsApp/deploy/license/.gitignore | 4 - SportsApp/deploy/license/progress.cfg | Bin 0 -> 5408 bytes SportsApp/deploy/scripts/startServer.sh | 100 - SportsApp/deploy/undeploy.sh | 6 - SportsApp/gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - SportsApp/gradlew | 234 - SportsApp/gradlew.bat | 89 - SportsApp/settings.gradle | 11 - SportsApp/webui/grid.js | 76 - SportsApp/webui/index.html | 63 - SportsApp/webui/progress.all.js | 14881 ---------------- 221 files changed, 420 insertions(+), 31903 deletions(-) delete mode 100644 SportsApp/.gitignore delete mode 100644 SportsApp/Sports/.dbconnection delete mode 100644 SportsApp/Sports/.gitattributes delete mode 100644 SportsApp/Sports/.gitignore delete mode 100644 SportsApp/Sports/.project delete mode 100644 SportsApp/Sports/.propath delete mode 100644 SportsApp/Sports/.scripts/common-build-tasks.xml delete mode 100644 SportsApp/Sports/.scripts/deploy-app.sh delete mode 100644 SportsApp/Sports/.scripts/dotpropath-to-pctpropath.xsl delete mode 100644 SportsApp/Sports/.scripts/propath.xml delete mode 100644 SportsApp/Sports/.scripts/standardpaths.xml delete mode 100644 SportsApp/Sports/.services/AppServer/Customer.pidl delete mode 100644 SportsApp/Sports/.services/AppServer/Item.pidl delete mode 100644 SportsApp/Sports/.services/AppServer/Order.pidl delete mode 100644 SportsApp/Sports/.services/AppServer/Salesrep.pidl delete mode 100644 SportsApp/Sports/.services/AppServer/State.pidl delete mode 100644 SportsApp/Sports/.services/Expose/rest/SportsService/resourceModel.xml delete mode 100644 SportsApp/Sports/.services/Expose/rest/SportsService/spring.xml delete mode 100644 SportsApp/Sports/.services/Sports.properties delete mode 100644 SportsApp/Sports/.services/adapters.pamf delete mode 100644 SportsApp/Sports/.settings/org.eclipse.wst.common.component delete mode 100644 SportsApp/Sports/.settings/org.eclipse.wst.common.project.facet.core.xml delete mode 100644 SportsApp/Sports/.settings/pasoe.appserver.settings.xml delete mode 100644 SportsApp/Sports/.settings/pasoe.config.xml delete mode 100644 SportsApp/Sports/AppServer/Customer.cls delete mode 100644 SportsApp/Sports/AppServer/GetCustOrders.p delete mode 100644 SportsApp/Sports/AppServer/Item.cls delete mode 100644 SportsApp/Sports/AppServer/Order.cls delete mode 100644 SportsApp/Sports/AppServer/Salesrep.cls delete mode 100644 SportsApp/Sports/AppServer/State.cls delete mode 100644 SportsApp/Sports/AppServer/UpdateCustOrders.p delete mode 100644 SportsApp/Sports/AppServer/customer.i delete mode 100644 SportsApp/Sports/AppServer/item.i delete mode 100644 SportsApp/Sports/AppServer/myCustOrder.i delete mode 100644 SportsApp/Sports/AppServer/order.i delete mode 100644 SportsApp/Sports/AppServer/salesrep.i delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/asordnum.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/asstate.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/crbin.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/crcust.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/cremp.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/crintr.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/crinv.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/critem.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/crlocdef.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/crord.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/crordl.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/crpo.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/crsuppl.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/crware.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/delcust.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/delinv.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/delitem.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/delord.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/delordl.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/delsup.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/ref_call.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/wrcust.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/writem.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/wrord.p delete mode 100644 SportsApp/Sports/AppServer/sports2020trgs/wrordl.p delete mode 100644 SportsApp/Sports/AppServer/state.i delete mode 100644 SportsApp/Sports/Dockerfile delete mode 100644 SportsApp/Sports/PASOEContent/META-INF/MANIFEST.MF delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/README delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/_oepingService/_oepingService.paar delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/README delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/adapters/web/README.txt delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-none.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-basic.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-none.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/classes/manifest.txt delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/home/ServerStatus.html delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/logging.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/metadata/README delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.csv delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/security.tld delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/spring/properties-loader.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/tlr/Merge.template delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/users.properties delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/web.xml delete mode 100644 SportsApp/Sports/PASOEContent/WEB-INF/web.xml-clientcert delete mode 100644 SportsApp/Sports/PASOEContent/favicon.ico delete mode 100644 SportsApp/Sports/PASOEContent/index.jsp delete mode 100644 SportsApp/Sports/PASOEContent/static/ServerStatus.html delete mode 100644 SportsApp/Sports/PASOEContent/static/SportsService.json delete mode 100644 SportsApp/Sports/PASOEContent/static/auth/login.html delete mode 100644 SportsApp/Sports/PASOEContent/static/auth/login.jsp delete mode 100644 SportsApp/Sports/PASOEContent/static/auth/loginfail.html delete mode 100644 SportsApp/Sports/PASOEContent/static/auth/logout.html delete mode 100644 SportsApp/Sports/PASOEContent/static/auth/logout.jsp delete mode 100644 SportsApp/Sports/PASOEContent/static/commonPageFooter.html delete mode 100644 SportsApp/Sports/PASOEContent/static/commonPageHeader.html delete mode 100644 SportsApp/Sports/PASOEContent/static/commonStyle.css delete mode 100644 SportsApp/Sports/PASOEContent/static/error/error401.html delete mode 100644 SportsApp/Sports/PASOEContent/static/error/error403.html delete mode 100644 SportsApp/Sports/PASOEContent/static/error/error404.html delete mode 100644 SportsApp/Sports/PASOEContent/static/error/error500.html delete mode 100644 SportsApp/Sports/PASOEContent/static/error/error503.html delete mode 100644 SportsApp/Sports/PASOEContent/static/home.html delete mode 100644 SportsApp/Sports/PASOEContent/static/home.jsp delete mode 100644 SportsApp/Sports/PASOEContent/static/images/Thumbs.db delete mode 100644 SportsApp/Sports/PASOEContent/static/images/appserver.png delete mode 100644 SportsApp/Sports/PASOEContent/static/images/appserver2.png delete mode 100644 SportsApp/Sports/PASOEContent/static/images/background.png delete mode 100644 SportsApp/Sports/PASOEContent/static/images/communities.png delete mode 100644 SportsApp/Sports/PASOEContent/static/images/content.png delete mode 100644 SportsApp/Sports/PASOEContent/static/images/documentation.png delete mode 100644 SportsApp/Sports/PASOEContent/static/images/gear.png delete mode 100644 SportsApp/Sports/PASOEContent/static/images/progress.png delete mode 100644 SportsApp/Sports/PASOEContent/static/images/qstart.png delete mode 100644 SportsApp/Sports/PASOEContent/static/images/videos.png delete mode 100644 SportsApp/Sports/PASOEContent/static/index.jsp delete mode 100644 SportsApp/Sports/PASOEContent/static/serverstatus.js delete mode 100644 SportsApp/Sports/PASOEContent/static/sessionsExceeded.jsp create mode 100644 SportsApp/Sports/build-output/AppServer/Customer.r create mode 100644 SportsApp/Sports/build-output/AppServer/GetCustOrders.r create mode 100644 SportsApp/Sports/build-output/AppServer/Item.r create mode 100644 SportsApp/Sports/build-output/AppServer/Order.r create mode 100644 SportsApp/Sports/build-output/AppServer/Salesrep.r create mode 100644 SportsApp/Sports/build-output/AppServer/State.r create mode 100644 SportsApp/Sports/build-output/AppServer/UpdateCustOrders.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/asordnum.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/asstate.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crbin.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crcust.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/cremp.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crintr.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crinv.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/critem.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crlocdef.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crord.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crordl.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crpo.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crsuppl.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crware.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delcust.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delinv.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delitem.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delord.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delordl.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delsup.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/ref_call.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/wrcust.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/writem.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/wrord.r create mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/wrordl.r create mode 100644 SportsApp/Sports/build-output/tests/AppServer/CustomerTest.r create mode 100644 SportsApp/Sports/build-output/tests/AppServer/OrderTest.r create mode 100644 SportsApp/Sports/build-output/tests/AppServer/SportsTestSuite.r delete mode 100644 SportsApp/Sports/build.config delete mode 100644 SportsApp/Sports/build.gradle delete mode 100644 SportsApp/Sports/conf/MANIFEST.MF delete mode 100644 SportsApp/Sports/conf/startup.pf delete mode 100644 SportsApp/Sports/gradle.properties delete mode 100644 SportsApp/Sports/gradle/wrapper/gradle-wrapper.jar delete mode 100644 SportsApp/Sports/gradle/wrapper/gradle-wrapper.properties delete mode 100644 SportsApp/Sports/gradlew delete mode 100644 SportsApp/Sports/gradlew.bat delete mode 100644 SportsApp/Sports/settings.gradle delete mode 100644 SportsApp/Sports/tests/AppServer/CustomerTest.cls delete mode 100644 SportsApp/Sports/tests/AppServer/OrderTest.cls delete mode 100644 SportsApp/Sports/tests/AppServer/SportsTestSuite.cls delete mode 100644 SportsApp/Sports/tests/CustomerTest.cls delete mode 100644 SportsApp/Sports/tests/OrderTest.cls delete mode 100644 SportsApp/Sports/tests/SportsTestSuite.cls delete mode 100644 SportsApp/Sports/tlr/merge.properties delete mode 100644 SportsApp/Sports/tlr/openedge.properties.merge create mode 100644 SportsApp/Sports/velocity.log delete mode 100644 SportsApp/build.gradle delete mode 100644 SportsApp/deploy/.gitignore delete mode 100644 SportsApp/deploy/Dockerfile delete mode 100644 SportsApp/deploy/ablapps/.gitignore delete mode 100644 SportsApp/deploy/deploy.sh delete mode 100644 SportsApp/deploy/docker-compose.yml delete mode 100644 SportsApp/deploy/license/.gitignore create mode 100644 SportsApp/deploy/license/progress.cfg delete mode 100644 SportsApp/deploy/scripts/startServer.sh delete mode 100644 SportsApp/deploy/undeploy.sh delete mode 100644 SportsApp/gradle/wrapper/gradle-wrapper.jar delete mode 100644 SportsApp/gradle/wrapper/gradle-wrapper.properties delete mode 100644 SportsApp/gradlew delete mode 100644 SportsApp/gradlew.bat delete mode 100644 SportsApp/settings.gradle delete mode 100644 SportsApp/webui/grid.js delete mode 100644 SportsApp/webui/index.html delete mode 100644 SportsApp/webui/progress.all.js diff --git a/SportsApp/.gitignore b/SportsApp/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/SportsApp/Sports/.dbconnection b/SportsApp/Sports/.dbconnection deleted file mode 100644 index 5c94f99..0000000 --- a/SportsApp/Sports/.dbconnection +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/SportsApp/Sports/.gitattributes b/SportsApp/Sports/.gitattributes deleted file mode 100644 index 00a51af..0000000 --- a/SportsApp/Sports/.gitattributes +++ /dev/null @@ -1,6 +0,0 @@ -# -# https://help.github.com/articles/dealing-with-line-endings/ -# -# These are explicitly windows files and should use crlf -*.bat text eol=crlf - diff --git a/SportsApp/Sports/.gitignore b/SportsApp/Sports/.gitignore deleted file mode 100644 index bddab10..0000000 --- a/SportsApp/Sports/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Ignore Gradle project-specific cache directory -.gradle - -# Ignore Gradle build output directory -build -build-output - -results.xml -velocity.log \ No newline at end of file diff --git a/SportsApp/Sports/.project b/SportsApp/Sports/.project deleted file mode 100644 index 138fd72..0000000 --- a/SportsApp/Sports/.project +++ /dev/null @@ -1,29 +0,0 @@ - - - Sports - - - - - - com.openedge.pdt.text.progressBuilder - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.wst.validation.validationbuilder - - - - - - com.openedge.pdt.text.progressNature - org.eclipse.wst.common.project.facet.core.nature - org.eclipse.wst.common.modulecore.ModuleCoreNature - - diff --git a/SportsApp/Sports/.propath b/SportsApp/Sports/.propath deleted file mode 100644 index 5ddc5cc..0000000 --- a/SportsApp/Sports/.propath +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/SportsApp/Sports/.scripts/common-build-tasks.xml b/SportsApp/Sports/.scripts/common-build-tasks.xml deleted file mode 100644 index b716aaf..0000000 --- a/SportsApp/Sports/.scripts/common-build-tasks.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SportsApp/Sports/.scripts/deploy-app.sh b/SportsApp/Sports/.scripts/deploy-app.sh deleted file mode 100644 index d19f11d..0000000 --- a/SportsApp/Sports/.scripts/deploy-app.sh +++ /dev/null @@ -1,27 +0,0 @@ -set -e -export DLC=/psc/dlc -export PATH=$DLC/bin:$PATH -export PROJECT_NAME=$(echo `pwd` | rev | cut -f2 -d'/' - | rev); - -# Create PASOE instance -export DLC=/psc/dlc &&\ - cd /psc/wrk &&\ - /psc/dlc/bin/pasman create -v oepas1 - -# Replace all occurances of logs directory to volume mount -find ./ -type f -exec sed -i -e 's/${catalina.base}\/logs/\/var\/log/gI' {} \; - -# Copy application files - startup parameters - REST service -cp /app/${PROJECT_NAME}.props /psc/wrk -cp /app/${PROJECT_NAME}.war /psc/wrk - -# Copy ABL files to PASInstance - extracting from zip file -unzip /app/${PROJECT_NAME}.zip -d /psc/wrk/oepas1/openedge - -# Update startup parameters -cd /psc/wrk &&\ -/psc/wrk/oepas1/bin/oeprop.sh -v -f /psc/wrk/${PROJECT_NAME}.props - -# Deploy REST service -cd /psc/wrk &&\ -/psc/wrk/oepas1/bin/tcman.sh deploy -v /psc/wrk/${PROJECT_NAME}.war \ No newline at end of file diff --git a/SportsApp/Sports/.scripts/dotpropath-to-pctpropath.xsl b/SportsApp/Sports/.scripts/dotpropath-to-pctpropath.xsl deleted file mode 100644 index 956ac5d..0000000 --- a/SportsApp/Sports/.scripts/dotpropath-to-pctpropath.xsl +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SportsApp/Sports/.scripts/propath.xml b/SportsApp/Sports/.scripts/propath.xml deleted file mode 100644 index 72eff0e..0000000 --- a/SportsApp/Sports/.scripts/propath.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/.scripts/standardpaths.xml b/SportsApp/Sports/.scripts/standardpaths.xml deleted file mode 100644 index 6acb72a..0000000 --- a/SportsApp/Sports/.scripts/standardpaths.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SportsApp/Sports/.services/AppServer/Customer.pidl b/SportsApp/Sports/.services/AppServer/Customer.pidl deleted file mode 100644 index 92093e4..0000000 --- a/SportsApp/Sports/.services/AppServer/Customer.pidl +++ /dev/null @@ -1,870 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Customer - - - cls - - Customer - - - count - 1 - - count - - filter - filter - 1 - 1 - - - numRecs - numRecs - 4 - 2 - - - - - CreateCustomer - 1 - - CreateCustomer - - dsCustomer - dsCustomer - 36 - 3 - - - - - id - 1 - 0 - CHARACTER - 0 - - - seq - 4 - 0 - INTEGER - 0 - - - CustNum - 4 - 0 - INTEGER - 0 - - - Name - 1 - 0 - CHARACTER - 0 - - - Address - 1 - 0 - CHARACTER - 0 - - - Address2 - 1 - 0 - CHARACTER - 0 - - - Balance - 5 - 0 - DECIMAL - 0 - - - City - 1 - 0 - CHARACTER - 0 - - - Comments - 1 - 0 - CHARACTER - 0 - - - Contact - 1 - 0 - CHARACTER - 0 - - - Country - 1 - 0 - CHARACTER - 0 - - - CreditLimit - 5 - 0 - DECIMAL - 0 - - - Discount - 4 - 0 - INTEGER - 0 - - - EmailAddress - 1 - 0 - CHARACTER - 0 - - - Fax - 1 - 0 - CHARACTER - 0 - - - Phone - 1 - 0 - CHARACTER - 0 - - - PostalCode - 1 - 0 - CHARACTER - 0 - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - State - 1 - 0 - CHARACTER - 0 - - - Terms - 1 - 0 - CHARACTER - 0 - - - - - - - - DeleteCustomer - 1 - - DeleteCustomer - - dsCustomer - dsCustomer - 36 - 3 - - - - - id - 1 - 0 - CHARACTER - 0 - - - seq - 4 - 0 - INTEGER - 0 - - - CustNum - 4 - 0 - INTEGER - 0 - - - Name - 1 - 0 - CHARACTER - 0 - - - Address - 1 - 0 - CHARACTER - 0 - - - Address2 - 1 - 0 - CHARACTER - 0 - - - Balance - 5 - 0 - DECIMAL - 0 - - - City - 1 - 0 - CHARACTER - 0 - - - Comments - 1 - 0 - CHARACTER - 0 - - - Contact - 1 - 0 - CHARACTER - 0 - - - Country - 1 - 0 - CHARACTER - 0 - - - CreditLimit - 5 - 0 - DECIMAL - 0 - - - Discount - 4 - 0 - INTEGER - 0 - - - EmailAddress - 1 - 0 - CHARACTER - 0 - - - Fax - 1 - 0 - CHARACTER - 0 - - - Phone - 1 - 0 - CHARACTER - 0 - - - PostalCode - 1 - 0 - CHARACTER - 0 - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - State - 1 - 0 - CHARACTER - 0 - - - Terms - 1 - 0 - CHARACTER - 0 - - - - - - - - ReadCustomer - 1 - - ReadCustomer - - filter - filter - 1 - 1 - - - dsCustomer - dsCustomer - 36 - 2 - - - - - id - 1 - 0 - CHARACTER - 0 - - - seq - 4 - 0 - INTEGER - 0 - - - CustNum - 4 - 0 - INTEGER - 0 - - - Name - 1 - 0 - CHARACTER - 0 - - - Address - 1 - 0 - CHARACTER - 0 - - - Address2 - 1 - 0 - CHARACTER - 0 - - - Balance - 5 - 0 - DECIMAL - 0 - - - City - 1 - 0 - CHARACTER - 0 - - - Comments - 1 - 0 - CHARACTER - 0 - - - Contact - 1 - 0 - CHARACTER - 0 - - - Country - 1 - 0 - CHARACTER - 0 - - - CreditLimit - 5 - 0 - DECIMAL - 0 - - - Discount - 4 - 0 - INTEGER - 0 - - - EmailAddress - 1 - 0 - CHARACTER - 0 - - - Fax - 1 - 0 - CHARACTER - 0 - - - Phone - 1 - 0 - CHARACTER - 0 - - - PostalCode - 1 - 0 - CHARACTER - 0 - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - State - 1 - 0 - CHARACTER - 0 - - - Terms - 1 - 0 - CHARACTER - 0 - - - - - - - - SubmitCustomer - 1 - - SubmitCustomer - - dsCustomer - dsCustomer - 36 - 3 - - - - - id - 1 - 0 - CHARACTER - 0 - - - seq - 4 - 0 - INTEGER - 0 - - - CustNum - 4 - 0 - INTEGER - 0 - - - Name - 1 - 0 - CHARACTER - 0 - - - Address - 1 - 0 - CHARACTER - 0 - - - Address2 - 1 - 0 - CHARACTER - 0 - - - Balance - 5 - 0 - DECIMAL - 0 - - - City - 1 - 0 - CHARACTER - 0 - - - Comments - 1 - 0 - CHARACTER - 0 - - - Contact - 1 - 0 - CHARACTER - 0 - - - Country - 1 - 0 - CHARACTER - 0 - - - CreditLimit - 5 - 0 - DECIMAL - 0 - - - Discount - 4 - 0 - INTEGER - 0 - - - EmailAddress - 1 - 0 - CHARACTER - 0 - - - Fax - 1 - 0 - CHARACTER - 0 - - - Phone - 1 - 0 - CHARACTER - 0 - - - PostalCode - 1 - 0 - CHARACTER - 0 - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - State - 1 - 0 - CHARACTER - 0 - - - Terms - 1 - 0 - CHARACTER - 0 - - - - - - - - UpdateCustomer - 1 - - UpdateCustomer - - dsCustomer - dsCustomer - 36 - 3 - - - - - id - 1 - 0 - CHARACTER - 0 - - - seq - 4 - 0 - INTEGER - 0 - - - CustNum - 4 - 0 - INTEGER - 0 - - - Name - 1 - 0 - CHARACTER - 0 - - - Address - 1 - 0 - CHARACTER - 0 - - - Address2 - 1 - 0 - CHARACTER - 0 - - - Balance - 5 - 0 - DECIMAL - 0 - - - City - 1 - 0 - CHARACTER - 0 - - - Comments - 1 - 0 - CHARACTER - 0 - - - Contact - 1 - 0 - CHARACTER - 0 - - - Country - 1 - 0 - CHARACTER - 0 - - - CreditLimit - 5 - 0 - DECIMAL - 0 - - - Discount - 4 - 0 - INTEGER - 0 - - - EmailAddress - 1 - 0 - CHARACTER - 0 - - - Fax - 1 - 0 - CHARACTER - 0 - - - Phone - 1 - 0 - CHARACTER - 0 - - - PostalCode - 1 - 0 - CHARACTER - 0 - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - State - 1 - 0 - CHARACTER - 0 - - - Terms - 1 - 0 - CHARACTER - 0 - - - - - - - - - - diff --git a/SportsApp/Sports/.services/AppServer/Item.pidl b/SportsApp/Sports/.services/AppServer/Item.pidl deleted file mode 100644 index 4ad7017..0000000 --- a/SportsApp/Sports/.services/AppServer/Item.pidl +++ /dev/null @@ -1,635 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Item - - - cls - - Item - - - CreateItem - 1 - - CreateItem - - dsItem - dsItem - 36 - 3 - - - - - Itemnum - 4 - 0 - INTEGER - 0 - - - ItemName - 1 - 0 - CHARACTER - 0 - - - Price - 5 - 0 - DECIMAL - 0 - - - Onhand - 4 - 0 - INTEGER - 0 - - - Allocated - 4 - 0 - INTEGER - 0 - - - ReOrder - 4 - 0 - INTEGER - 0 - - - OnOrder - 4 - 0 - INTEGER - 0 - - - CatPage - 4 - 0 - INTEGER - 0 - - - CatDescription - 1 - 0 - CHARACTER - 0 - - - Category1 - 1 - 0 - CHARACTER - 0 - - - Category2 - 1 - 0 - CHARACTER - 0 - - - Special - 1 - 0 - CHARACTER - 0 - - - Weight - 5 - 0 - DECIMAL - 0 - - - Minqty - 4 - 0 - INTEGER - 0 - - - - - - - - DeleteItem - 1 - - DeleteItem - - dsItem - dsItem - 36 - 3 - - - - - Itemnum - 4 - 0 - INTEGER - 0 - - - ItemName - 1 - 0 - CHARACTER - 0 - - - Price - 5 - 0 - DECIMAL - 0 - - - Onhand - 4 - 0 - INTEGER - 0 - - - Allocated - 4 - 0 - INTEGER - 0 - - - ReOrder - 4 - 0 - INTEGER - 0 - - - OnOrder - 4 - 0 - INTEGER - 0 - - - CatPage - 4 - 0 - INTEGER - 0 - - - CatDescription - 1 - 0 - CHARACTER - 0 - - - Category1 - 1 - 0 - CHARACTER - 0 - - - Category2 - 1 - 0 - CHARACTER - 0 - - - Special - 1 - 0 - CHARACTER - 0 - - - Weight - 5 - 0 - DECIMAL - 0 - - - Minqty - 4 - 0 - INTEGER - 0 - - - - - - - - ReadItem - 1 - - ReadItem - - filter - filter - 1 - 1 - - - dsItem - dsItem - 36 - 2 - - - - - Itemnum - 4 - 0 - INTEGER - 0 - - - ItemName - 1 - 0 - CHARACTER - 0 - - - Price - 5 - 0 - DECIMAL - 0 - - - Onhand - 4 - 0 - INTEGER - 0 - - - Allocated - 4 - 0 - INTEGER - 0 - - - ReOrder - 4 - 0 - INTEGER - 0 - - - OnOrder - 4 - 0 - INTEGER - 0 - - - CatPage - 4 - 0 - INTEGER - 0 - - - CatDescription - 1 - 0 - CHARACTER - 0 - - - Category1 - 1 - 0 - CHARACTER - 0 - - - Category2 - 1 - 0 - CHARACTER - 0 - - - Special - 1 - 0 - CHARACTER - 0 - - - Weight - 5 - 0 - DECIMAL - 0 - - - Minqty - 4 - 0 - INTEGER - 0 - - - - - - - - SubmitItem - 1 - - SubmitItem - - dsItem - dsItem - 36 - 3 - - - - - Itemnum - 4 - 0 - INTEGER - 0 - - - ItemName - 1 - 0 - CHARACTER - 0 - - - Price - 5 - 0 - DECIMAL - 0 - - - Onhand - 4 - 0 - INTEGER - 0 - - - Allocated - 4 - 0 - INTEGER - 0 - - - ReOrder - 4 - 0 - INTEGER - 0 - - - OnOrder - 4 - 0 - INTEGER - 0 - - - CatPage - 4 - 0 - INTEGER - 0 - - - CatDescription - 1 - 0 - CHARACTER - 0 - - - Category1 - 1 - 0 - CHARACTER - 0 - - - Category2 - 1 - 0 - CHARACTER - 0 - - - Special - 1 - 0 - CHARACTER - 0 - - - Weight - 5 - 0 - DECIMAL - 0 - - - Minqty - 4 - 0 - INTEGER - 0 - - - - - - - - UpdateItem - 1 - - UpdateItem - - dsItem - dsItem - 36 - 3 - - - - - Itemnum - 4 - 0 - INTEGER - 0 - - - ItemName - 1 - 0 - CHARACTER - 0 - - - Price - 5 - 0 - DECIMAL - 0 - - - Onhand - 4 - 0 - INTEGER - 0 - - - Allocated - 4 - 0 - INTEGER - 0 - - - ReOrder - 4 - 0 - INTEGER - 0 - - - OnOrder - 4 - 0 - INTEGER - 0 - - - CatPage - 4 - 0 - INTEGER - 0 - - - CatDescription - 1 - 0 - CHARACTER - 0 - - - Category1 - 1 - 0 - CHARACTER - 0 - - - Category2 - 1 - 0 - CHARACTER - 0 - - - Special - 1 - 0 - CHARACTER - 0 - - - Weight - 5 - 0 - DECIMAL - 0 - - - Minqty - 4 - 0 - INTEGER - 0 - - - - - - - - - - diff --git a/SportsApp/Sports/.services/AppServer/Order.pidl b/SportsApp/Sports/.services/AppServer/Order.pidl deleted file mode 100644 index a24b84f..0000000 --- a/SportsApp/Sports/.services/AppServer/Order.pidl +++ /dev/null @@ -1,670 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Order - - - cls - - Order - - - CreateOrder - 1 - - CreateOrder - - dsOrder - dsOrder - 36 - 3 - - - - - Ordernum - 4 - 0 - INTEGER - 0 - - - CustNum - 4 - 0 - INTEGER - 0 - - - OrderDate - 2 - 0 - DATE - 0 - - - ShipDate - 2 - 0 - DATE - 0 - - - PromiseDate - 2 - 0 - DATE - 0 - - - Carrier - 1 - 0 - CHARACTER - 0 - - - Instructions - 1 - 0 - CHARACTER - 0 - - - PO - 1 - 0 - CHARACTER - 0 - - - Terms - 1 - 0 - CHARACTER - 0 - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - BillToID - 4 - 0 - INTEGER - 0 - - - ShipToID - 4 - 0 - INTEGER - 0 - - - OrderStatus - 1 - 0 - CHARACTER - 0 - - - WarehouseNum - 4 - 0 - INTEGER - 0 - - - Creditcard - 1 - 0 - CHARACTER - 0 - - - - - - - - DeleteOrder - 1 - - DeleteOrder - - dsOrder - dsOrder - 36 - 3 - - - - - Ordernum - 4 - 0 - INTEGER - 0 - - - CustNum - 4 - 0 - INTEGER - 0 - - - OrderDate - 2 - 0 - DATE - 0 - - - ShipDate - 2 - 0 - DATE - 0 - - - PromiseDate - 2 - 0 - DATE - 0 - - - Carrier - 1 - 0 - CHARACTER - 0 - - - Instructions - 1 - 0 - CHARACTER - 0 - - - PO - 1 - 0 - CHARACTER - 0 - - - Terms - 1 - 0 - CHARACTER - 0 - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - BillToID - 4 - 0 - INTEGER - 0 - - - ShipToID - 4 - 0 - INTEGER - 0 - - - OrderStatus - 1 - 0 - CHARACTER - 0 - - - WarehouseNum - 4 - 0 - INTEGER - 0 - - - Creditcard - 1 - 0 - CHARACTER - 0 - - - - - - - - ReadOrder - 1 - - ReadOrder - - filter - filter - 1 - 1 - - - dsOrder - dsOrder - 36 - 2 - - - - - Ordernum - 4 - 0 - INTEGER - 0 - - - CustNum - 4 - 0 - INTEGER - 0 - - - OrderDate - 2 - 0 - DATE - 0 - - - ShipDate - 2 - 0 - DATE - 0 - - - PromiseDate - 2 - 0 - DATE - 0 - - - Carrier - 1 - 0 - CHARACTER - 0 - - - Instructions - 1 - 0 - CHARACTER - 0 - - - PO - 1 - 0 - CHARACTER - 0 - - - Terms - 1 - 0 - CHARACTER - 0 - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - BillToID - 4 - 0 - INTEGER - 0 - - - ShipToID - 4 - 0 - INTEGER - 0 - - - OrderStatus - 1 - 0 - CHARACTER - 0 - - - WarehouseNum - 4 - 0 - INTEGER - 0 - - - Creditcard - 1 - 0 - CHARACTER - 0 - - - - - - - - SubmitOrder - 1 - - SubmitOrder - - dsOrder - dsOrder - 36 - 3 - - - - - Ordernum - 4 - 0 - INTEGER - 0 - - - CustNum - 4 - 0 - INTEGER - 0 - - - OrderDate - 2 - 0 - DATE - 0 - - - ShipDate - 2 - 0 - DATE - 0 - - - PromiseDate - 2 - 0 - DATE - 0 - - - Carrier - 1 - 0 - CHARACTER - 0 - - - Instructions - 1 - 0 - CHARACTER - 0 - - - PO - 1 - 0 - CHARACTER - 0 - - - Terms - 1 - 0 - CHARACTER - 0 - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - BillToID - 4 - 0 - INTEGER - 0 - - - ShipToID - 4 - 0 - INTEGER - 0 - - - OrderStatus - 1 - 0 - CHARACTER - 0 - - - WarehouseNum - 4 - 0 - INTEGER - 0 - - - Creditcard - 1 - 0 - CHARACTER - 0 - - - - - - - - UpdateOrder - 1 - - UpdateOrder - - dsOrder - dsOrder - 36 - 3 - - - - - Ordernum - 4 - 0 - INTEGER - 0 - - - CustNum - 4 - 0 - INTEGER - 0 - - - OrderDate - 2 - 0 - DATE - 0 - - - ShipDate - 2 - 0 - DATE - 0 - - - PromiseDate - 2 - 0 - DATE - 0 - - - Carrier - 1 - 0 - CHARACTER - 0 - - - Instructions - 1 - 0 - CHARACTER - 0 - - - PO - 1 - 0 - CHARACTER - 0 - - - Terms - 1 - 0 - CHARACTER - 0 - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - BillToID - 4 - 0 - INTEGER - 0 - - - ShipToID - 4 - 0 - INTEGER - 0 - - - OrderStatus - 1 - 0 - CHARACTER - 0 - - - WarehouseNum - 4 - 0 - INTEGER - 0 - - - Creditcard - 1 - 0 - CHARACTER - 0 - - - - - - - - - - diff --git a/SportsApp/Sports/.services/AppServer/Salesrep.pidl b/SportsApp/Sports/.services/AppServer/Salesrep.pidl deleted file mode 100644 index c7e5253..0000000 --- a/SportsApp/Sports/.services/AppServer/Salesrep.pidl +++ /dev/null @@ -1,285 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Salesrep - - - cls - - Salesrep - - - CreateSalesrep - 1 - - CreateSalesrep - - dsSalesrep - dsSalesrep - 36 - 3 - - - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - RepName - 1 - 0 - CHARACTER - 0 - - - Region - 1 - 0 - CHARACTER - 0 - - - MonthQuota - 4 - 12 - INTEGER - 0 - - - - - - - - DeleteSalesrep - 1 - - DeleteSalesrep - - dsSalesrep - dsSalesrep - 36 - 3 - - - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - RepName - 1 - 0 - CHARACTER - 0 - - - Region - 1 - 0 - CHARACTER - 0 - - - MonthQuota - 4 - 12 - INTEGER - 0 - - - - - - - - ReadSalesrep - 1 - - ReadSalesrep - - filter - filter - 1 - 1 - - - dsSalesrep - dsSalesrep - 36 - 2 - - - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - RepName - 1 - 0 - CHARACTER - 0 - - - Region - 1 - 0 - CHARACTER - 0 - - - MonthQuota - 4 - 12 - INTEGER - 0 - - - - - - - - SubmitSalesrep - 1 - - SubmitSalesrep - - dsSalesrep - dsSalesrep - 36 - 3 - - - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - RepName - 1 - 0 - CHARACTER - 0 - - - Region - 1 - 0 - CHARACTER - 0 - - - MonthQuota - 4 - 12 - INTEGER - 0 - - - - - - - - UpdateSalesrep - 1 - - UpdateSalesrep - - dsSalesrep - dsSalesrep - 36 - 3 - - - - - SalesRep - 1 - 0 - CHARACTER - 0 - - - RepName - 1 - 0 - CHARACTER - 0 - - - Region - 1 - 0 - CHARACTER - 0 - - - MonthQuota - 4 - 12 - INTEGER - 0 - - - - - - - - - - diff --git a/SportsApp/Sports/.services/AppServer/State.pidl b/SportsApp/Sports/.services/AppServer/State.pidl deleted file mode 100644 index 68da206..0000000 --- a/SportsApp/Sports/.services/AppServer/State.pidl +++ /dev/null @@ -1,250 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - State - - - cls - - State - - - CreateState - 1 - - CreateState - - dsState - dsState - 36 - 3 - - - - - State - 1 - 0 - CHARACTER - 0 - - - StateName - 1 - 0 - CHARACTER - 0 - - - Region - 1 - 0 - CHARACTER - 0 - - - - - - - - DeleteState - 1 - - DeleteState - - dsState - dsState - 36 - 3 - - - - - State - 1 - 0 - CHARACTER - 0 - - - StateName - 1 - 0 - CHARACTER - 0 - - - Region - 1 - 0 - CHARACTER - 0 - - - - - - - - ReadState - 1 - - ReadState - - filter - filter - 1 - 1 - - - dsState - dsState - 36 - 2 - - - - - State - 1 - 0 - CHARACTER - 0 - - - StateName - 1 - 0 - CHARACTER - 0 - - - Region - 1 - 0 - CHARACTER - 0 - - - - - - - - SubmitState - 1 - - SubmitState - - dsState - dsState - 36 - 3 - - - - - State - 1 - 0 - CHARACTER - 0 - - - StateName - 1 - 0 - CHARACTER - 0 - - - Region - 1 - 0 - CHARACTER - 0 - - - - - - - - UpdateState - 1 - - UpdateState - - dsState - dsState - 36 - 3 - - - - - State - 1 - 0 - CHARACTER - 0 - - - StateName - 1 - 0 - CHARACTER - 0 - - - Region - 1 - 0 - CHARACTER - 0 - - - - - - - - - - diff --git a/SportsApp/Sports/.services/Expose/rest/SportsService/resourceModel.xml b/SportsApp/Sports/.services/Expose/rest/SportsService/resourceModel.xml deleted file mode 100644 index 8b6c5ad..0000000 --- a/SportsApp/Sports/.services/Expose/rest/SportsService/resourceModel.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/.services/Expose/rest/SportsService/spring.xml b/SportsApp/Sports/.services/Expose/rest/SportsService/spring.xml deleted file mode 100644 index 0901f25..0000000 --- a/SportsApp/Sports/.services/Expose/rest/SportsService/spring.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - com.progress.rest.adapters.oe.Open4GLException - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/.services/Sports.properties b/SportsApp/Sports/.services/Sports.properties deleted file mode 100644 index e259010..0000000 --- a/SportsApp/Sports/.services/Sports.properties +++ /dev/null @@ -1,4 +0,0 @@ -#Properties generated using Progress Tooling -#Wed Feb 06 14:18:49 IST 2019 -Spring.XmlFile.Extensions=xml -archiveName=Sports diff --git a/SportsApp/Sports/.services/adapters.pamf b/SportsApp/Sports/.services/adapters.pamf deleted file mode 100644 index af6713b..0000000 --- a/SportsApp/Sports/.services/adapters.pamf +++ /dev/null @@ -1,392 +0,0 @@ - - - - - - - - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsCustomer']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsCustomer']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsCustomer']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsCustomer']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsCustomer']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsCustomer']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsCustomer']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsCustomer']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsCustomer']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['numRecs']}" target="${json.object['response'].integervalue['numRecs']}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsItem']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsItem']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsItem']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsItem']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsItem']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsItem']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsItem']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsItem']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsItem']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsOrder']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsOrder']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsOrder']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsOrder']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsOrder']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsOrder']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsOrder']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsOrder']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsOrder']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsSalesrep']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsSalesrep']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsSalesrep']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsSalesrep']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsSalesrep']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsSalesrep']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsSalesrep']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsSalesrep']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsSalesrep']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsState']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsState']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsState']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsState']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${rest.queryparam['filter']}" target="${idl.param['filter']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsState']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsState']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsState']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - <?xml version="1.0" encoding="UTF-8"?> -<mapping:messageMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mapping="http://www.progress.com/caf/mapping1.0" xmlns:metadata="http://www.progress.com/caf/mapping1.0/toolingMetadata"> - <metadata:toolingMetadata xsi:type="metadata:toolingMetadata"> - <metadata:mapInput/> - <metadata:mapOutput/> - </metadata:toolingMetadata> - <mapping:mapInput> - <mapping:rule source="${http.body}" target="${idl.param['dsState']}"/> - </mapping:mapInput> - <mapping:mapOutput> - <mapping:rule source="${idl.param['dsState']}" target="${http.body}"/> - </mapping:mapOutput> - <mapping:mapFault name="default_fault"/> -</mapping:messageMapping> - /SportsService - - - - - <?xml version="1.0" encoding="UTF-8" standalone="no"?><oe_mobile><service><name>SportsService</name><resources><path>AppServer/Customer.cls</path><path>AppServer/Item.cls</path><path>AppServer/Order.cls</path><path>AppServer/Salesrep.cls</path><path>AppServer/State.cls</path></resources></service></oe_mobile> - - - - - - - - - - - - - Sports.properties - - diff --git a/SportsApp/Sports/.settings/org.eclipse.wst.common.component b/SportsApp/Sports/.settings/org.eclipse.wst.common.component deleted file mode 100644 index 8f2d00d..0000000 --- a/SportsApp/Sports/.settings/org.eclipse.wst.common.component +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/SportsApp/Sports/.settings/org.eclipse.wst.common.project.facet.core.xml b/SportsApp/Sports/.settings/org.eclipse.wst.common.project.facet.core.xml deleted file mode 100644 index 53435d4..0000000 --- a/SportsApp/Sports/.settings/org.eclipse.wst.common.project.facet.core.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/SportsApp/Sports/.settings/pasoe.appserver.settings.xml b/SportsApp/Sports/.settings/pasoe.appserver.settings.xml deleted file mode 100644 index 862d40e..0000000 --- a/SportsApp/Sports/.settings/pasoe.appserver.settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/SportsApp/Sports/.settings/pasoe.config.xml b/SportsApp/Sports/.settings/pasoe.config.xml deleted file mode 100644 index 91613ca..0000000 --- a/SportsApp/Sports/.settings/pasoe.config.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - Sports - diff --git a/SportsApp/Sports/AppServer/Customer.cls b/SportsApp/Sports/AppServer/Customer.cls deleted file mode 100644 index 692df17..0000000 --- a/SportsApp/Sports/AppServer/Customer.cls +++ /dev/null @@ -1,253 +0,0 @@ -@program FILE(name="Customer.cls", module="AppServer"). -@openapi.openedge.export FILE(type="REST", executionMode="singleton", useReturnValue="false", writeDataSetBeforeImage="false"). -@progress.service.resource FILE(name="Customer", URI="/Customer", schemaName="dsCustomer", schemaFile="Sports/AppServer/customer.i"). - -USING Progress.Lang.*. - -USING OpenEdge.BusinessLogic.BusinessEntity. -USING Progress.Json.ObjectModel.*. - -BLOCK-LEVEL ON ERROR UNDO, THROW. - -CLASS Customer INHERITS BusinessEntity: - - {"customer.i"} - - DEFINE DATA-SOURCE srcCustomer FOR Customer. - - DEFINE VARIABLE iSeq AS INTEGER NO-UNDO. - - CONSTRUCTOR PUBLIC Customer(): - - DEFINE VAR hDataSourceArray AS HANDLE NO-UNDO EXTENT 1. - DEFINE VAR cSkipListArray AS CHAR NO-UNDO EXTENT 1. - - SUPER (DATASET dsCustomer:HANDLE). - - /* Data Source for each table in dataset. - Should be in table order as defined in DataSet */ - hDataSourceArray[1] = DATA-SOURCE srcCustomer:HANDLE. - - /* Skip-list entry array for each table in DataSet. - Should be in temp-table order as defined in DataSet */ - /* Each skip-list entry is a comma-separated list of field names - to be ignored in the CREATE statement */ - - cSkipListArray[1] = "CustNum". - - THIS-OBJECT:ProDataSource = hDataSourceArray. - THIS-OBJECT:SkipList = cSkipListArray. - - END CONSTRUCTOR. - - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="read", URI="?filter=~{filter~}", alias="", mediaType="application/json"). - @openapi.openedge.method.property (name="mappingType", value="JFP"). - @openapi.openedge.method.property (name="capabilities", value="ablFilter,top,skip,id,orderBy"). - METHOD PUBLIC VOID ReadCustomer( - INPUT filter AS CHARACTER, - OUTPUT DATASET dsCustomer): - - IF filter BEGINS "~{" THEN - THIS-OBJECT:JFPFillMethod (INPUT filter). - ELSE - DO: - BUFFER ttCustomer:HANDLE:BATCH-SIZE = 0. - BUFFER ttCustomer:SET-CALLBACK ("AFTER-ROW-FILL", "AddIdField"). - SUPER:ReadData(filter). - END. - END METHOD. - - /* Other CUD and Submit operation methods */ - - /*------------------------------------------------------------------------------ - Purpose: Create one or more new records - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="create", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID CreateCustomer(INPUT-OUTPUT DATASET dsCustomer): - - SUPER:CreateData(DATASET dsCustomer BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Update one or more records - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="update", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID UpdateCustomer(INPUT-OUTPUT DATASET dsCustomer): - - SUPER:UpdateData(DATASET dsCustomer BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Delete a record - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="delete", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID DeleteCustomer(INPUT-OUTPUT DATASET dsCustomer): - - SUPER:DeleteData(DATASET dsCustomer BY-REFERENCE). - END METHOD. - - - METHOD PRIVATE VOID JFPFillMethod(INPUT filter AS CHARACTER): - - DEFINE VARIABLE jsonParser AS ObjectModelParser NO-UNDO. - DEFINE VARIABLE jsonObject AS JsonObject NO-UNDO. - DEFINE VARIABLE cWhere AS CHARACTER NO-UNDO. - DEFINE VARIABLE hQuery AS HANDLE NO-UNDO. - DEFINE VARIABLE lUseReposition AS LOGICAL NO-UNDO. - DEFINE VARIABLE iCount AS INTEGER NO-UNDO. - DEFINE VARIABLE ablFilter AS CHARACTER NO-UNDO. - DEFINE VARIABLE id AS CHARACTER INITIAL ? NO-UNDO. - DEFINE VARIABLE iMaxRows AS INTEGER INITIAL ? NO-UNDO. - DEFINE VARIABLE iSkipRows AS INTEGER INITIAL ? NO-UNDO. - DEFINE VARIABLE cOrderBy AS CHARACTER INITIAL "" NO-UNDO. - - /* purge any existing data */ - EMPTY TEMP-TABLE ttCustomer. - - jsonParser = NEW ObjectModelParser(). - jsonObject = CAST(jsonParser:Parse(filter), jsonObject). - iMaxRows = jsonObject:GetInteger("top") NO-ERROR. - iSkipRows = jsonObject:GetInteger("skip") NO-ERROR. - ablFilter = jsonObject:GetCharacter("ablFilter") NO-ERROR. - id = jsonObject:GetCharacter("id") NO-ERROR. - cOrderBy = jsonObject:GetCharacter("orderBy") NO-ERROR. - cWhere = "WHERE " + ablFilter NO-ERROR. - - IF cOrderBy > "" THEN - DO: - cOrderBy = REPLACE(cOrderBy, ",", " by "). - cOrderBy = "by " + cOrderBy + " ". - /* NOTE: id and seq fields should be removed from - cWhere and cOrderBy */ - cOrderBy = REPLACE(cOrderBy, "by id desc", ""). - cOrderBy = REPLACE(cOrderBy, "by id ", ""). - cOrderBy = REPLACE(cOrderBy, "by seq desc", ""). - cOrderBy = REPLACE(cOrderBy, "by seq ", ""). - END. - - lUseReposition = iSkipRows <> ?. - - IF iMaxRows <> ? AND iMaxRows > 0 THEN - DO: - BUFFER ttCustomer:HANDLE:BATCH-SIZE = iMaxRows. - END. - ELSE - DO: - IF id > "" THEN - BUFFER ttCustomer:HANDLE:BATCH-SIZE = 1. - ELSE - BUFFER ttCustomer:HANDLE:BATCH-SIZE = 0. - END. - - BUFFER ttCustomer:ATTACH-DATA-SOURCE(DATA-SOURCE srcCustomer:HANDLE). - - IF cOrderBy = ? THEN cOrderBy = "". - cWhere = IF cWhere > "" THEN (cWhere + " " + cOrderBy) - ELSE ("WHERE " + cOrderBy). - DATA-SOURCE srcCustomer:FILL-WHERE-STRING = cWhere. - - IF lUseReposition THEN - DO: - hQuery = DATA-SOURCE srcCustomer:QUERY. - hQuery:QUERY-OPEN. - - IF id > "" AND id <> "?" THEN - DO: - hQuery:REPOSITION-TO-ROWID(TO-ROWID(id)). - END. - ELSE IF iSkipRows <> ? AND iSkipRows > 0 THEN - DO: - hQuery:REPOSITION-TO-ROW(iSkipRows). - IF NOT AVAILABLE Customer THEN - hQuery:GET-NEXT() NO-ERROR. - END. - - iCount = 0. - REPEAT WHILE NOT hQuery:QUERY-OFF-END AND iCount < iMaxRows: - hQuery:GET-NEXT () NO-ERROR. - IF AVAILABLE Customer THEN - DO: - CREATE ttCustomer. - BUFFER-COPY Customer TO ttCustomer. - ASSIGN - ttCustomer.id = STRING(ROWID(Customer)) - iSeq = iSeq + 1 - ttCustomer.seq = iSeq. - END. - iCount = iCount + 1. - END. - END. - ELSE - DO: - IF id > "" THEN DATA-SOURCE srcCustomer:RESTART-ROWID(1) - = TO-ROWID ((id)). - BUFFER ttCustomer:SET-CALLBACK ("AFTER-ROW-FILL", "AddIdField"). - DATASET dsCustomer:FILL(). - END. - - FINALLY: - BUFFER ttCustomer:DETACH-DATA-SOURCE(). - END FINALLY. - - END METHOD. - - METHOD PUBLIC VOID AddIdField (INPUT DATASET dsCustomer): - ASSIGN - ttCustomer.id = STRING(ROWID(Customer)) - iSeq = iSeq + 1 - ttCustomer.seq = iSeq. - END. - - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="false"). - @progress.service.resourceMapping(type="REST", operation="invoke", URI="/count?filter=~{filter~}", alias="", mediaType="application/json"). - METHOD PUBLIC VOID count( INPUT filter AS CHARACTER, OUTPUT numRecs AS INTEGER): - DEFINE VARIABLE jsonParser AS ObjectModelParser NO-UNDO. - DEFINE VARIABLE jsonObject AS JsonObject NO-UNDO. - DEFINE VARIABLE ablFilter AS CHARACTER NO-UNDO. - DEFINE VARIABLE cWhere AS CHARACTER NO-UNDO. - DEFINE VARIABLE qh AS HANDLE NO-UNDO. - - IF filter BEGINS "WHERE " THEN - cWhere = filter. - ELSE IF filter BEGINS "~{" THEN - DO: - jsonParser = NEW ObjectModelParser(). - jsonObject = CAST(jsonParser:Parse(filter), jsonObject). - ablFilter = jsonObject:GetCharacter("ablFilter") NO-ERROR. - cWhere = "WHERE " + ablFilter. - END. - ELSE IF filter NE "" THEN - DO: - /* Use filter as WHERE clause */ - cWhere = "WHERE " + filter. - END. - - CREATE QUERY qh. - qh:SET-BUFFERS(BUFFER Customer:HANDLE). - qh:QUERY-PREPARE("PRESELECT EACH Customer " + cWhere). - qh:QUERY-OPEN (). - numRecs = qh:NUM-RESULTS. - - END METHOD. - - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitSCustomer", alias="", mediaType="application/json"). - METHOD PUBLIC VOID SubmitCustomer(INPUT-OUTPUT DATASET dsCustomer): - SUPER:Submit(DATASET dsCustomer BY-REFERENCE). - FOR EACH ttCustomer: - FIND FIRST customer WHERE customer.custnum = ttCustomer.custnum NO-LOCK NO-ERROR. - IF AVAILABLE customer THEN - DO: - ttCustomer.id = STRING(ROWID(Customer)). - END. - END. - END METHOD. - -END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/AppServer/GetCustOrders.p b/SportsApp/Sports/AppServer/GetCustOrders.p deleted file mode 100644 index 1631159..0000000 --- a/SportsApp/Sports/AppServer/GetCustOrders.p +++ /dev/null @@ -1,54 +0,0 @@ - - -/** GetCustOrders.p **/ - -{"myCustOrder.i"} - - -DEFINE INPUT PARAMETER whereStr AS CHAR. -DEFINE OUTPUT PARAMETER DATASET-HANDLE Hdl. - - -DEFINE VAR prepareExp AS CHAR NO-UNDO. -DEFINE VAR orderCnt AS INT INIT 0. - -DEF VAR retok AS LOG. -DEF VAR hCustOrders AS HANDLE. -DEF VAR hqCust AS HANDLE. -DEF VAR sError AS CHAR. - -DEFINE QUERY qCust FOR Customer. -hqCust = QUERY qCust:HANDLE. - -prepareExp = "FOR EACH Customer". - -IF whereStr BEGINS "WHERE " THEN - prepareExp = prepareExp + " " + whereStr. -ELSE IF whereStr NE "" THEN - prepareExp = prepareExp + " WHERE " + whereStr. - -MESSAGE "GetCustOrders.p: prepareExp is: " prepareExp. - -retok = hqCust:QUERY-PREPARE(prepareExp). - - -IF NOT retok THEN -DO: - // sError = "QUERY-PREPARE failed" . - RETURN. -END. - -DEFINE DATA-SOURCE dsCust FOR QUERY qCust. -DEFINE DATA-SOURCE dsOrder FOR Order. - -DATASET dsCustomerOrder:EMPTY-DATASET(). - -BUFFER ttCustomer:HANDLE:ATTACH-DATA-SOURCE(DATA-SOURCE dsCust:HANDLE,?,?,?). - -BUFFER ttOrder:HANDLE:ATTACH-DATA-SOURCE(DATA-SOURCE dsOrder:HANDLE,?,?,?). - -retok = DATASET dsCustomerOrder:FILL(). - -Hdl = DATASET dsCustomerOrder:HANDLE. - - diff --git a/SportsApp/Sports/AppServer/Item.cls b/SportsApp/Sports/AppServer/Item.cls deleted file mode 100644 index acf1256..0000000 --- a/SportsApp/Sports/AppServer/Item.cls +++ /dev/null @@ -1,118 +0,0 @@ - - /*------------------------------------------------------------------------ - File : Item - Syntax : - Author(s) : anikumar - Created : Fri Jul 27 14:16:02 EDT 2018 - Notes : - ----------------------------------------------------------------------*/ - -@program FILE(name="Item.cls", module="AppServer"). -@openapi.openedge.export FILE(type="REST", executionMode="singleton", useReturnValue="false", writeDataSetBeforeImage="false"). -@progress.service.resource FILE(name="Item", URI="/Item", schemaName="dsItem", schemaFile="Sports/AppServer/item.i"). - -USING Progress.Lang.*. -USING OpenEdge.BusinessLogic.BusinessEntity. - -BLOCK-LEVEL ON ERROR UNDO, THROW. - -CLASS Item INHERITS BusinessEntity: - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - {"item.i"} - - DEFINE DATA-SOURCE srcItem FOR sports.Item. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - CONSTRUCTOR PUBLIC Item(): - - DEFINE VARIABLE hDataSourceArray AS HANDLE NO-UNDO EXTENT 1. - DEFINE VARIABLE cSkipListArray AS CHARACTER NO-UNDO EXTENT 1. - - SUPER (DATASET dsItem:HANDLE). - - /* Data Source for each table in dataset. Should be in table order as defined - in DataSet */ - hDataSourceArray[1] = DATA-SOURCE srcItem:HANDLE. - - - /* Skip-list entry for each table in dataset. Should be in temp-table order - as defined in DataSet */ - /* Each skip-list entry is a comma-separated list of field names, to be - ignored in create stmt */ - - cSkipListArray[1] = "". - - - THIS-OBJECT:ProDataSource = hDataSourceArray. - THIS-OBJECT:SkipList = cSkipListArray. - - END CONSTRUCTOR. - - /*------------------------------------------------------------------------------ - Purpose: Get one or more records, based on a filter string - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="read", URI="?filter=~{filter~}", alias="", mediaType="application/json"). - METHOD PUBLIC VOID ReadItem( - INPUT filter AS CHARACTER, - OUTPUT DATASET dsItem): - - SUPER:ReadData(filter). - - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Create one or more new records - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="create", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID CreateItem(INPUT-OUTPUT DATASET dsItem): - - SUPER:CreateData(DATASET dsItem BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Update one or more records - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="update", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID UpdateItem(INPUT-OUTPUT DATASET dsItem): - - SUPER:UpdateData(DATASET dsItem BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Delete a record - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="delete", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID DeleteItem(INPUT-OUTPUT DATASET dsItem): - - SUPER:DeleteData(DATASET dsItem BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Submit a record - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitItem", alias="", mediaType="application/json"). - METHOD PUBLIC VOID SubmitItem(INPUT-OUTPUT DATASET dsItem): - - SUPER:Submit(DATASET dsItem BY-REFERENCE). - END METHOD. - - -END CLASS. diff --git a/SportsApp/Sports/AppServer/Order.cls b/SportsApp/Sports/AppServer/Order.cls deleted file mode 100644 index 8e111b5..0000000 --- a/SportsApp/Sports/AppServer/Order.cls +++ /dev/null @@ -1,118 +0,0 @@ - - /*------------------------------------------------------------------------ - File : Order - Syntax : - Author(s) : anikumar - Created : Fri Jul 27 14:16:21 EDT 2018 - Notes : - ----------------------------------------------------------------------*/ - -@program FILE(name="Order.cls", module="AppServer"). -@openapi.openedge.export FILE(type="REST", executionMode="singleton", useReturnValue="false", writeDataSetBeforeImage="false"). -@progress.service.resource FILE(name="Order", URI="/Order", schemaName="dsOrder", schemaFile="Sports/AppServer/order.i"). - -USING Progress.Lang.*. -USING OpenEdge.BusinessLogic.BusinessEntity. - -BLOCK-LEVEL ON ERROR UNDO, THROW. - -CLASS Order INHERITS BusinessEntity: - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - {"order.i"} - - DEFINE DATA-SOURCE srcOrder FOR sports.Order. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - CONSTRUCTOR PUBLIC Order(): - - DEFINE VARIABLE hDataSourceArray AS HANDLE NO-UNDO EXTENT 1. - DEFINE VARIABLE cSkipListArray AS CHARACTER NO-UNDO EXTENT 1. - - SUPER (DATASET dsOrder:HANDLE). - - /* Data Source for each table in dataset. Should be in table order as defined - in DataSet */ - hDataSourceArray[1] = DATA-SOURCE srcOrder:HANDLE. - - - /* Skip-list entry for each table in dataset. Should be in temp-table order - as defined in DataSet */ - /* Each skip-list entry is a comma-separated list of field names, to be - ignored in create stmt */ - - cSkipListArray[1] = "". - - - THIS-OBJECT:ProDataSource = hDataSourceArray. - THIS-OBJECT:SkipList = cSkipListArray. - - END CONSTRUCTOR. - - /*------------------------------------------------------------------------------ - Purpose: Get one or more records, based on a filter string - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="read", URI="?filter=~{filter~}", alias="", mediaType="application/json"). - METHOD PUBLIC VOID ReadOrder( - INPUT filter AS CHARACTER, - OUTPUT DATASET dsOrder): - - SUPER:ReadData(filter). - - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Create one or more new records - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="create", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID CreateOrder(INPUT-OUTPUT DATASET dsOrder): - - SUPER:CreateData(DATASET dsOrder BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Update one or more records - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="update", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID UpdateOrder(INPUT-OUTPUT DATASET dsOrder): - - SUPER:UpdateData(DATASET dsOrder BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Delete a record - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="delete", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID DeleteOrder(INPUT-OUTPUT DATASET dsOrder): - - SUPER:DeleteData(DATASET dsOrder BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Submit a record - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitOrder", alias="", mediaType="application/json"). - METHOD PUBLIC VOID SubmitOrder(INPUT-OUTPUT DATASET dsOrder): - - SUPER:Submit(DATASET dsOrder BY-REFERENCE). - END METHOD. - - -END CLASS. diff --git a/SportsApp/Sports/AppServer/Salesrep.cls b/SportsApp/Sports/AppServer/Salesrep.cls deleted file mode 100644 index 3e8557e..0000000 --- a/SportsApp/Sports/AppServer/Salesrep.cls +++ /dev/null @@ -1,118 +0,0 @@ - - /*------------------------------------------------------------------------ - File : Salesrep - Syntax : - Author(s) : anikumar - Created : Fri Jul 27 14:15:22 EDT 2018 - Notes : - ----------------------------------------------------------------------*/ - -@program FILE(name="Salesrep.cls", module="AppServer"). -@openapi.openedge.export FILE(type="REST", executionMode="singleton", useReturnValue="false", writeDataSetBeforeImage="false"). -@progress.service.resource FILE(name="Salesrep", URI="/Salesrep", schemaName="dsSalesrep", schemaFile="Sports/AppServer/salesrep.i"). - -USING Progress.Lang.*. -USING OpenEdge.BusinessLogic.BusinessEntity. - -BLOCK-LEVEL ON ERROR UNDO, THROW. - -CLASS Salesrep INHERITS BusinessEntity: - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - {"salesrep.i"} - - DEFINE DATA-SOURCE srcSalesrep FOR sports.Salesrep. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - CONSTRUCTOR PUBLIC Salesrep(): - - DEFINE VARIABLE hDataSourceArray AS HANDLE NO-UNDO EXTENT 1. - DEFINE VARIABLE cSkipListArray AS CHARACTER NO-UNDO EXTENT 1. - - SUPER (DATASET dsSalesrep:HANDLE). - - /* Data Source for each table in dataset. Should be in table order as defined - in DataSet */ - hDataSourceArray[1] = DATA-SOURCE srcSalesrep:HANDLE. - - - /* Skip-list entry for each table in dataset. Should be in temp-table order - as defined in DataSet */ - /* Each skip-list entry is a comma-separated list of field names, to be - ignored in create stmt */ - - cSkipListArray[1] = "". - - - THIS-OBJECT:ProDataSource = hDataSourceArray. - THIS-OBJECT:SkipList = cSkipListArray. - - END CONSTRUCTOR. - - /*------------------------------------------------------------------------------ - Purpose: Get one or more records, based on a filter string - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="read", URI="?filter=~{filter~}", alias="", mediaType="application/json"). - METHOD PUBLIC VOID ReadSalesrep( - INPUT filter AS CHARACTER, - OUTPUT DATASET dsSalesrep): - - SUPER:ReadData(filter). - - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Create one or more new records - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="create", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID CreateSalesrep(INPUT-OUTPUT DATASET dsSalesrep): - - SUPER:CreateData(DATASET dsSalesrep BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Update one or more records - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="update", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID UpdateSalesrep(INPUT-OUTPUT DATASET dsSalesrep): - - SUPER:UpdateData(DATASET dsSalesrep BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Delete a record - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="delete", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID DeleteSalesrep(INPUT-OUTPUT DATASET dsSalesrep): - - SUPER:DeleteData(DATASET dsSalesrep BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Submit a record - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitSalesrep", alias="", mediaType="application/json"). - METHOD PUBLIC VOID SubmitSalesrep(INPUT-OUTPUT DATASET dsSalesrep): - - SUPER:Submit(DATASET dsSalesrep BY-REFERENCE). - END METHOD. - - -END CLASS. diff --git a/SportsApp/Sports/AppServer/State.cls b/SportsApp/Sports/AppServer/State.cls deleted file mode 100644 index 23cf237..0000000 --- a/SportsApp/Sports/AppServer/State.cls +++ /dev/null @@ -1,118 +0,0 @@ - - /*------------------------------------------------------------------------ - File : State - Syntax : - Author(s) : anikumar - Created : Fri Jul 27 14:14:58 EDT 2018 - Notes : - ----------------------------------------------------------------------*/ - -@program FILE(name="State.cls", module="AppServer"). -@openapi.openedge.export FILE(type="REST", executionMode="singleton", useReturnValue="false", writeDataSetBeforeImage="false"). -@progress.service.resource FILE(name="State", URI="/State", schemaName="dsState", schemaFile="Sports/AppServer/state.i"). - -USING Progress.Lang.*. -USING OpenEdge.BusinessLogic.BusinessEntity. - -BLOCK-LEVEL ON ERROR UNDO, THROW. - -CLASS State INHERITS BusinessEntity: - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - {"state.i"} - - DEFINE DATA-SOURCE srcState FOR sports.State. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - CONSTRUCTOR PUBLIC State(): - - DEFINE VARIABLE hDataSourceArray AS HANDLE NO-UNDO EXTENT 1. - DEFINE VARIABLE cSkipListArray AS CHARACTER NO-UNDO EXTENT 1. - - SUPER (DATASET dsState:HANDLE). - - /* Data Source for each table in dataset. Should be in table order as defined - in DataSet */ - hDataSourceArray[1] = DATA-SOURCE srcState:HANDLE. - - - /* Skip-list entry for each table in dataset. Should be in temp-table order - as defined in DataSet */ - /* Each skip-list entry is a comma-separated list of field names, to be - ignored in create stmt */ - - cSkipListArray[1] = "". - - - THIS-OBJECT:ProDataSource = hDataSourceArray. - THIS-OBJECT:SkipList = cSkipListArray. - - END CONSTRUCTOR. - - /*------------------------------------------------------------------------------ - Purpose: Get one or more records, based on a filter string - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="read", URI="?filter=~{filter~}", alias="", mediaType="application/json"). - METHOD PUBLIC VOID ReadState( - INPUT filter AS CHARACTER, - OUTPUT DATASET dsState): - - SUPER:ReadData(filter). - - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Create one or more new records - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="create", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID CreateState(INPUT-OUTPUT DATASET dsState): - - SUPER:CreateData(DATASET dsState BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Update one or more records - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="update", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID UpdateState(INPUT-OUTPUT DATASET dsState): - - SUPER:UpdateData(DATASET dsState BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Delete a record - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="delete", URI="", alias="", mediaType="application/json"). - METHOD PUBLIC VOID DeleteState(INPUT-OUTPUT DATASET dsState): - - SUPER:DeleteData(DATASET dsState BY-REFERENCE). - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: Submit a record - Notes: - ------------------------------------------------------------------------------*/ - @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true"). - @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitState", alias="", mediaType="application/json"). - METHOD PUBLIC VOID SubmitState(INPUT-OUTPUT DATASET dsState): - - SUPER:Submit(DATASET dsState BY-REFERENCE). - END METHOD. - - -END CLASS. diff --git a/SportsApp/Sports/AppServer/UpdateCustOrders.p b/SportsApp/Sports/AppServer/UpdateCustOrders.p deleted file mode 100644 index a353ce3..0000000 --- a/SportsApp/Sports/AppServer/UpdateCustOrders.p +++ /dev/null @@ -1,53 +0,0 @@ - - - - -/** UpdateCustOrders.p **/ - - {"myCustOrder.i"} - - -FUNCTION GetErrorMsg RETURNS CHAR: - DEFINE VAR errMsg AS CHAR. - - IF ERROR-STATUS:NUM-MESSAGES >= 1 THEN - errMsg = ERROR-STATUS:GET-MESSAGE(1). - ELSE - errMsg = RETURN-VALUE. - - IF errMsg EQ ? THEN - errMsg = "Error occurred". - - RETURN errMsg. -END FUNCTION. - - -DEFINE INPUT-OUTPUT PARAMETER DATASET FOR dsCustomerOrder. -DEFINE OUTPUT PARAMETER statusMsg AS CHAR INIT ?. - - DEFINE DATA-SOURCE dsCust FOR Customer. - DEFINE DATA-SOURCE dsOrder FOR Order. - - BUFFER ttCustomer:ATTACH-DATA-SOURCE (DATA-SOURCE dsCust:HANDLE). - BUFFER ttOrder:ATTACH-DATA-SOURCE (DATA-SOURCE dsOrder:HANDLE). - - FOR EACH bttCustomer: - MESSAGE "UpdateCustOrders.p, UPDATING CustNum: " bttCustomer.Custnum. - BUFFER bttCustomer:SAVE-ROW-CHANGES(1, "CustNum") NO-ERROR. - IF BUFFER bttCustomer:ERROR THEN - statusMsg = GetErrorMsg(). - END. - - FOR EACH bttOrder: - BUFFER bttOrder:SAVE-ROW-CHANGES(1, "Ordernum") NO-ERROR. - IF BUFFER bttOrder:ERROR THEN - statusMsg = GetErrorMsg(). - END. - - BUFFER bttCustomer:DETACH-DATA-SOURCE(). - BUFFER bttOrder:DETACH-DATA-SOURCE(). - - IF statusMsg EQ ? THEN - statusMsg = "Okay". - - diff --git a/SportsApp/Sports/AppServer/customer.i b/SportsApp/Sports/AppServer/customer.i deleted file mode 100644 index 320d00d..0000000 --- a/SportsApp/Sports/AppServer/customer.i +++ /dev/null @@ -1,27 +0,0 @@ -@openapi.openedge.entity.property (name="idProperty", value="id"). -DEFINE TEMP-TABLE ttCustomer BEFORE-TABLE bttCustomer - FIELD id AS CHARACTER - FIELD seq AS INTEGER INITIAL ? - FIELD CustNum AS INTEGER INITIAL "0" LABEL "Cust Num" - FIELD Name AS CHARACTER LABEL "Name" - FIELD Address AS CHARACTER LABEL "Address" - FIELD Address2 AS CHARACTER LABEL "Address2" - FIELD Balance AS DECIMAL INITIAL "0" LABEL "Balance" - FIELD City AS CHARACTER LABEL "City" - FIELD Comments AS CHARACTER LABEL "Comments" - FIELD Contact AS CHARACTER LABEL "Contact" - FIELD Country AS CHARACTER INITIAL "USA" LABEL "Country" - FIELD CreditLimit AS DECIMAL INITIAL "1500" LABEL "Credit Limit" - FIELD Discount AS INTEGER INITIAL "0" LABEL "Discount" - FIELD EmailAddress AS CHARACTER LABEL "Email" - FIELD Fax AS CHARACTER LABEL "Fax" - FIELD Phone AS CHARACTER LABEL "Phone" - FIELD PostalCode AS CHARACTER LABEL "Postal Code" - FIELD SalesRep AS CHARACTER LABEL "Sales Rep" - FIELD State AS CHARACTER LABEL "State" - FIELD Terms AS CHARACTER INITIAL "Net30" LABEL "Terms" - INDEX seq IS PRIMARY UNIQUE seq - /* INDEX CustNum IS UNIQUE CustNum */ - . - -DEFINE DATASET dsCustomer FOR ttCustomer. \ No newline at end of file diff --git a/SportsApp/Sports/AppServer/item.i b/SportsApp/Sports/AppServer/item.i deleted file mode 100644 index acf0ce7..0000000 --- a/SportsApp/Sports/AppServer/item.i +++ /dev/null @@ -1,44 +0,0 @@ - - /*------------------------------------------------------------------------ - File : Item - Purpose : - Syntax : - Description : - Author(s) : Administrator - Created : Fri Jul 27 14:16:02 EDT 2018 - Notes : - ----------------------------------------------------------------------*/ - - /* *************************** Definitions ************************** */ - - /* ******************** Preprocessor Definitions ******************** */ - - /* *************************** Main Block *************************** */ - - /** Dynamically generated schema file **/ - -@openapi.openedge.entity.primarykey (fields="Itemnum"). - -DEFINE TEMP-TABLE ttItem BEFORE-TABLE bttItem -FIELD Itemnum AS INTEGER INITIAL "0" LABEL "Item Num" -FIELD ItemName AS CHARACTER LABEL "Item Name" -FIELD Price AS DECIMAL INITIAL "0" LABEL "Price" -FIELD Onhand AS INTEGER INITIAL "0" LABEL "On Hand" -FIELD Allocated AS INTEGER INITIAL "0" LABEL "Allocated" -FIELD ReOrder AS INTEGER INITIAL "0" LABEL "Re Order" -FIELD OnOrder AS INTEGER INITIAL "0" LABEL "On Order" -FIELD CatPage AS INTEGER INITIAL "0" LABEL "Cat Page" -FIELD CatDescription AS CHARACTER LABEL "Cat-Description" -FIELD Category1 AS CHARACTER LABEL "Category1" -FIELD Category2 AS CHARACTER LABEL "Category2" -FIELD Special AS CHARACTER LABEL "Special" -FIELD Weight AS DECIMAL INITIAL "0" LABEL "Weight" -FIELD Minqty AS INTEGER INITIAL "0" LABEL "Min Qty" -INDEX CatDescription CatDescription ASCENDING -INDEX Category2ItemName Category2 ASCENDING ItemName ASCENDING -INDEX CategoryItemName Category1 ASCENDING ItemName ASCENDING -INDEX ItemName ItemName ASCENDING -INDEX ItemNum IS PRIMARY UNIQUE Itemnum ASCENDING . - - -DEFINE DATASET dsItem FOR ttItem. \ No newline at end of file diff --git a/SportsApp/Sports/AppServer/myCustOrder.i b/SportsApp/Sports/AppServer/myCustOrder.i deleted file mode 100644 index 91b1cdf..0000000 --- a/SportsApp/Sports/AppServer/myCustOrder.i +++ /dev/null @@ -1,34 +0,0 @@ - -DEFINE TEMP-TABLE ttCustomer BEFORE-TABLE bttCustomer - FIELD CustNum AS INTEGER INITIAL "0" LABEL "Cust Num" - FIELD Name AS CHARACTER LABEL "Name" - FIELD Address AS CHARACTER LABEL "Address" - FIELD Address2 AS CHARACTER LABEL "Address2" - FIELD Balance AS DECIMAL INITIAL "0" LABEL "Balance" - FIELD City AS CHARACTER LABEL "City" - FIELD Contact AS CHARACTER LABEL "Contact" - FIELD Country AS CHARACTER INITIAL "USA" LABEL "Country" - FIELD CreditLimit AS DECIMAL INITIAL "1500" LABEL "Credit Limit" - FIELD Discount AS INTEGER INITIAL "0" LABEL "Discount" - FIELD EmailAddress AS CHARACTER LABEL "Email" - FIELD Phone AS CHARACTER LABEL "Phone" - - INDEX CustNum IS UNIQUE CustNum. - - -DEFINE TEMP-TABLE ttOrder BEFORE-TABLE bttOrder - FIELD Ordernum AS INTEGER - FIELD CustNum AS INTEGER - FIELD OrderDate AS DATE - FIELD SalesRep AS CHARACTER - FIELD OrderStatus AS CHARACTER INITIAL "Ordered" - - INDEX CustOrderIdx IS UNIQUE CustNum Ordernum - INDEX OrdernumIdx IS UNIQUE PRIMARY Ordernum. - - -DEFINE DATASET dsCustomerOrder - FOR ttCustomer, ttOrder - DATA-RELATION custOrdRel FOR ttCustomer, ttOrder - RELATION-FIELDS (CustNum, CustNum). - diff --git a/SportsApp/Sports/AppServer/order.i b/SportsApp/Sports/AppServer/order.i deleted file mode 100644 index f36375b..0000000 --- a/SportsApp/Sports/AppServer/order.i +++ /dev/null @@ -1,45 +0,0 @@ - - /*------------------------------------------------------------------------ - File : Order - Purpose : - Syntax : - Description : - Author(s) : Administrator - Created : Fri Jul 27 14:16:21 EDT 2018 - Notes : - ----------------------------------------------------------------------*/ - - /* *************************** Definitions ************************** */ - - /* ******************** Preprocessor Definitions ******************** */ - - /* *************************** Main Block *************************** */ - - /** Dynamically generated schema file **/ - -@openapi.openedge.entity.primarykey (fields="Ordernum"). - -DEFINE TEMP-TABLE ttOrder BEFORE-TABLE bttOrder -FIELD Ordernum AS INTEGER INITIAL "0" LABEL "Order Num" -FIELD CustNum AS INTEGER INITIAL "0" LABEL "Cust Num" -FIELD OrderDate AS DATE INITIAL "TODAY" LABEL "Ordered" -FIELD ShipDate AS DATE LABEL "Shipped" -FIELD PromiseDate AS DATE LABEL "Promised" -FIELD Carrier AS CHARACTER LABEL "Carrier" -FIELD Instructions AS CHARACTER LABEL "Instructions" -FIELD PO AS CHARACTER LABEL "PO" -FIELD Terms AS CHARACTER INITIAL "Net30" LABEL "Terms" -FIELD SalesRep AS CHARACTER LABEL "Sales Rep" -FIELD BillToID AS INTEGER INITIAL "0" LABEL "Bill To ID" -FIELD ShipToID AS INTEGER INITIAL "0" LABEL "Ship To ID" -FIELD OrderStatus AS CHARACTER INITIAL "Ordered" LABEL "Order Status" -FIELD WarehouseNum AS INTEGER INITIAL "0" LABEL "Warehouse Num" -FIELD Creditcard AS CHARACTER INITIAL "Visa" LABEL "Credit Card" -INDEX CustOrder IS UNIQUE CustNum ASCENDING Ordernum ASCENDING -INDEX OrderDate OrderDate ASCENDING -INDEX OrderNum IS PRIMARY UNIQUE Ordernum ASCENDING -INDEX OrderStatus OrderStatus ASCENDING -INDEX SalesRep SalesRep ASCENDING . - - -DEFINE DATASET dsOrder FOR ttOrder. \ No newline at end of file diff --git a/SportsApp/Sports/AppServer/salesrep.i b/SportsApp/Sports/AppServer/salesrep.i deleted file mode 100644 index e4e301b..0000000 --- a/SportsApp/Sports/AppServer/salesrep.i +++ /dev/null @@ -1,30 +0,0 @@ - - /*------------------------------------------------------------------------ - File : Salesrep - Purpose : - Syntax : - Description : - Author(s) : Administrator - Created : Fri Jul 27 14:15:22 EDT 2018 - Notes : - ----------------------------------------------------------------------*/ - - /* *************************** Definitions ************************** */ - - /* ******************** Preprocessor Definitions ******************** */ - - /* *************************** Main Block *************************** */ - - /** Dynamically generated schema file **/ - -@openapi.openedge.entity.primarykey (fields="SalesRep"). - -DEFINE TEMP-TABLE ttSalesrep BEFORE-TABLE bttSalesrep -FIELD SalesRep AS CHARACTER LABEL "Sales Rep" -FIELD RepName AS CHARACTER LABEL "Rep Name" -FIELD Region AS CHARACTER LABEL "Region" -FIELD MonthQuota AS INTEGER EXTENT 12 INITIAL "0" LABEL "Month Quota" -INDEX SalesRep IS PRIMARY UNIQUE SalesRep ASCENDING . - - -DEFINE DATASET dsSalesrep FOR ttSalesrep. \ No newline at end of file diff --git a/SportsApp/Sports/AppServer/sports2020trgs/asordnum.p b/SportsApp/Sports/AppServer/sports2020trgs/asordnum.p deleted file mode 100644 index 2f5c212..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/asordnum.p +++ /dev/null @@ -1,12 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: asordnum.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR ASSIGN OF OrderLine.Ordernum. - -MESSAGE "Order Number Assigned". diff --git a/SportsApp/Sports/AppServer/sports2020trgs/asstate.p b/SportsApp/Sports/AppServer/sports2020trgs/asstate.p deleted file mode 100644 index d8947d6..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/asstate.p +++ /dev/null @@ -1,31 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: asstate.p -** Descript: To test, enter an Invalid Two-Character State Abbrev. -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Assign OF Customer.State. - -IF Customer.Country = "USA" AND NOT (CAN-FIND(State OF Customer)) -THEN DO: - MESSAGE "Illegal State name for the U.S.A." - VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. - RETURN ERROR. -END. - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crbin.p b/SportsApp/Sports/AppServer/sports2020trgs/crbin.p deleted file mode 100644 index 0b4399a..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/crbin.p +++ /dev/null @@ -1,15 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: crbin.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Create OF Bin. - -/* Automatically Increment Customer Number using NextCustNum Sequence */ - -ASSIGN Bin.BinNum = NEXT-VALUE(NextBinNum). - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crcust.p b/SportsApp/Sports/AppServer/sports2020trgs/crcust.p deleted file mode 100644 index 7637d5e..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/crcust.p +++ /dev/null @@ -1,15 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: crcust.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Create OF Customer. - -/* Automatically Increment Customer Number using NextCustNum Sequence */ - -ASSIGN Customer.CustNum = NEXT-VALUE(NextCustNum). - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/cremp.p b/SportsApp/Sports/AppServer/sports2020trgs/cremp.p deleted file mode 100644 index 4167953..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/cremp.p +++ /dev/null @@ -1,4 +0,0 @@ -TRIGGER PROCEDURE FOR CREATE OF Employee. - -ASSIGN Employee.EmpNum = NEXT-VALUE(NextEmpNum). - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crintr.p b/SportsApp/Sports/AppServer/sports2020trgs/crintr.p deleted file mode 100644 index 7024793..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/crintr.p +++ /dev/null @@ -1,4 +0,0 @@ -TRIGGER PROCEDURE FOR CREATE OF InventoryTrans. - -ASSIGN invtransnum = NEXT-VALUE(NextInvTransNum). - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crinv.p b/SportsApp/Sports/AppServer/sports2020trgs/crinv.p deleted file mode 100644 index 3c4aead..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/crinv.p +++ /dev/null @@ -1,14 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: crinv.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Create OF Invoice. - -/* Automatically increment Invoice Number using Next-Inv-Num Sequence */ - -ASSIGN invoice.invoicenum = NEXT-VALUE(NEXTINVNUM). diff --git a/SportsApp/Sports/AppServer/sports2020trgs/critem.p b/SportsApp/Sports/AppServer/sports2020trgs/critem.p deleted file mode 100644 index 9381318..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/critem.p +++ /dev/null @@ -1,14 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: critem.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Create OF Item. - -/* Automatically assign a unique item number using NextItemNum Sequence */ - -ASSIGN Item.ItemNum = NEXT-VALUE(NextItemNum). diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crlocdef.p b/SportsApp/Sports/AppServer/sports2020trgs/crlocdef.p deleted file mode 100644 index 39deb61..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/crlocdef.p +++ /dev/null @@ -1,4 +0,0 @@ -TRIGGER PROCEDURE FOR CREATE OF LocalDefault. - -ASSIGN LocalDefault.LocalDefNum = NEXT-VALUE(NextLocalDefNum). - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crord.p b/SportsApp/Sports/AppServer/sports2020trgs/crord.p deleted file mode 100644 index f758e21..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/crord.p +++ /dev/null @@ -1,19 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: crord.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Create OF Order. - -/* Automatically Increment Order-Number using Next-Ord-Num Sequence */ - -ASSIGN order.ordernum = NEXT-VALUE(NextOrdNum) - -/* Set Order Date to TODAY, Promise Date to 2 weeks from TODAY */ - -order.orderdate = TODAY -order.promisedate = TODAY + 14. diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crordl.p b/SportsApp/Sports/AppServer/sports2020trgs/crordl.p deleted file mode 100644 index 7483c16..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/crordl.p +++ /dev/null @@ -1,11 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: crordl.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Create OF OrderLine. - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crpo.p b/SportsApp/Sports/AppServer/sports2020trgs/crpo.p deleted file mode 100644 index 28f341d..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/crpo.p +++ /dev/null @@ -1,4 +0,0 @@ -TRIGGER PROCEDURE FOR CREATE OF PurchaseOrder. - -ASSIGN PurchaseOrder.PONum = NEXT-VALUE(NextPONum). - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crsuppl.p b/SportsApp/Sports/AppServer/sports2020trgs/crsuppl.p deleted file mode 100644 index 204b800..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/crsuppl.p +++ /dev/null @@ -1,4 +0,0 @@ -TRIGGER PROCEDURE FOR CREATE OF Supplier. - -ASSIGN Supplier.SupplierIDNum = NEXT-VALUE(NextSupplNum). - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crware.p b/SportsApp/Sports/AppServer/sports2020trgs/crware.p deleted file mode 100644 index 1016d99..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/crware.p +++ /dev/null @@ -1,3 +0,0 @@ -TRIGGER PROCEDURE FOR CREATE OF Warehouse. - -ASSIGN Warehouse.WarehouseNum = NEXT-VALUE(NextWareNum). diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delcust.p b/SportsApp/Sports/AppServer/sports2020trgs/delcust.p deleted file mode 100644 index a835cdf..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/delcust.p +++ /dev/null @@ -1,51 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: delcust.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER Procedure FOR Delete OF Customer. - -/* Variable Definitions */ - -DEFINE VARIABLE answer AS LOGICAL. - -/* Customer record cannot be deleted if outstanding invoices are found */ - -FIND FIRST invoice OF customer NO-ERROR. -IF AVAILABLE invoice THEN DO: - IF invoice.amount <= invoice.totalpaid + invoice.adjustment THEN DO: - MESSAGE "Invoice OK, looking for Orders..." - VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. - FIND FIRST order OF customer NO-ERROR. - IF NOT AVAILABLE order THEN DO: - RETURN. - END. - ELSE DO: - MESSAGE "Open orders exist for Customer " customer.custnum - ". Cannot delete." - VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. - RETURN ERROR. - END. - END. - ELSE DO: - MESSAGE "Outstanding Unpaid Invoice Exists, Cannot Delete" - VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. - RETURN ERROR. - END. -END. -ELSE DO: - FIND FIRST order OF customer NO-ERROR. - IF NOT AVAILABLE order THEN DO: - RETURN. - END. - ELSE DO: - MESSAGE "Open orders exist for Customer " customer.custnum - ". Cannot delete." - VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. - RETURN ERROR. - END. -END. diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delinv.p b/SportsApp/Sports/AppServer/sports2020trgs/delinv.p deleted file mode 100644 index cf63502..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/delinv.p +++ /dev/null @@ -1,19 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: delinv.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Delete OF Invoice. - -/* Invoices cannot be deleted if the Invoice amount exceeds Total-Paid + Adjustment */ - -IF Invoice.Amount > Invoice.TotalPaid + Invoice.Adjustment -THEN DO: - MESSAGE "The Invoice Amount cannot be greater than TotalPaid + Adjustment" - VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. - RETURN ERROR. -END. diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delitem.p b/SportsApp/Sports/AppServer/sports2020trgs/delitem.p deleted file mode 100644 index 5b676d3..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/delitem.p +++ /dev/null @@ -1,7 +0,0 @@ -TRIGGER PROCEDURE FOR DELETE OF Item. - -FOR EACH bin OF item: - - DELETE bin. - -END. /*for each bin*/ diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delord.p b/SportsApp/Sports/AppServer/sports2020trgs/delord.p deleted file mode 100644 index fa221ca..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/delord.p +++ /dev/null @@ -1,29 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: delord.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Delete OF Order. - -/* When Orders are deleted, associated Order detail lines (OrderLine) - * are also deleted. - */ - -MESSAGE "Deleting Order" OrderNum VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. -FOR EACH OrderLine OF Order: - DELETE OrderLine. -END. - - - - - - - - - - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delordl.p b/SportsApp/Sports/AppServer/sports2020trgs/delordl.p deleted file mode 100644 index 6819558..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/delordl.p +++ /dev/null @@ -1,16 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: delord.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Delete OF OrderLine. - -/* Trigger provides an information message when orderlines are deleted */ - -MESSAGE "Deleting Order Line:" Linenum "Order Num:" OrderNum - VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delsup.p b/SportsApp/Sports/AppServer/sports2020trgs/delsup.p deleted file mode 100644 index 24cb58b..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/delsup.p +++ /dev/null @@ -1,28 +0,0 @@ -TRIGGER PROCEDURE FOR DELETE OF Supplier. - -FIND FIRST purchaseorder OF supplier WHERE postatus <> "Received" -NO-LOCK NO-ERROR. - -IF AVAIL purchaseorder THEN DO: - - MESSAGE "Supplier can not be deleted." - "There is at least one PO that has not been received." - VIEW-AS ALERT-BOX ERROR BUTTONS OK. - RETURN ERROR. - -END. /*if avail purchaseorder*/ - -ELSE DO: - /*delete received po*/ - - FOR EACH purchaseorder OF Supplier: - - FOR EACH poline OF purchaseorder: - DELETE poline. - END. /*for each poline*/ - - DELETE purchaseorder. - - END. /*for each purchaseorder of supplier*/ - -END. /*else do*/ diff --git a/SportsApp/Sports/AppServer/sports2020trgs/ref_call.p b/SportsApp/Sports/AppServer/sports2020trgs/ref_call.p deleted file mode 100644 index cf6549b..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/ref_call.p +++ /dev/null @@ -1,13 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: ref_call.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -/* Assign trigger for the Ref-Call table */ -TRIGGER PROCEDURE FOR Create OF REFCALL. - -ASSIGN RefCall.CallNum = STRING(NEXT-VALUE(NextRefNum)). diff --git a/SportsApp/Sports/AppServer/sports2020trgs/wrcust.p b/SportsApp/Sports/AppServer/sports2020trgs/wrcust.p deleted file mode 100644 index 61133eb..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/wrcust.p +++ /dev/null @@ -1,52 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: wrcust.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Write OF Customer OLD BUFFER oldCustomer. - -/* Variable Definitions */ - -DEFINE VARIABLE i AS INTEGER INITIAL 0. -DEFINE VARIABLE Outstanding AS INTEGER INITIAL 0. - -/* Check to see if the user changed the Customer Number */ - -IF Customer.CustNum <> oldCustomer.CustNum AND oldCustomer.CustNum <> 0 -THEN DO: - - /* If user changed the Customer Number, find related orders and change */ - /* their customer numbers. */ - - FOR EACH order OF oldCustomer: - Order.CustNum = Customer.CustNum. - i = i + 1. - END. - IF i > 0 THEN - MESSAGE i "orders changed to reflect the new customer number!" - VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. -END. - -/* Ensure that the Credit Limit value is always Greater than the sum of this - * Customer's Outstanding Balance - */ - -FOR EACH Order OF Customer: - FOR EACH OrderLine OF Order where order.shipdate EQ ?: - Outstanding = Outstanding + OrderLine.ExtendedPrice. - END. -END. -FOR EACH Invoice OF Customer: - Outstanding = Outstanding + ( Amount - ( TotalPaid + Adjustment )). -END. - -IF Customer.CreditLimit < Outstanding THEN DO: - MESSAGE "This Customer has an outstanding balance of: " Outstanding - ". The Credit Limit MUST exceed this amount!" - VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. - RETURN ERROR. -END. diff --git a/SportsApp/Sports/AppServer/sports2020trgs/writem.p b/SportsApp/Sports/AppServer/sports2020trgs/writem.p deleted file mode 100644 index f9980cc..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/writem.p +++ /dev/null @@ -1,54 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: writem.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Write OF Item. - -/* - * Generate Po if there is not enough qty - */ - -IF Item.MinQty > - ((Item.OnHand - Item.Allocated) + item.onorder) THEN DO: - - FIND FIRST supplieritemxref where supplieritemxref.itemnum = - item.itemnum NO-LOCK NO-ERROR. - - IF AVAIL supplieritemxref THEN DO: - - FIND supplier WHERE supplier.supplieridnum = - supplieritemxref.supplieridnum NO-LOCK NO-ERROR. - - IF AVAIL supplier THEN DO: - CREATE purchaseorder. - ASSIGN - purchaseorder.DateEntered = today - purchaseorder.POStatus = "Ordered" - purchaseorder.SupplierIDNum = supplieritemxref.supplieridnum. - - CREATE poline. - ASSIGN - poline.ponum = purchaseorder.ponum - poline.linenum = 1 - poline.Discount = supplier.discount - poline.Itemnum = item.itemnum - poline.Price = item.price - poline.qty = item.reorder - poline.ExtendedPrice = (item.price * poline.qty) * (1 - supplier.discount). - item.onorder = item.onorder + item.reorder. - - /****** - MESSAGE "Purchase Order: " purchaseorder.ponum " for " Item.ItemName - ", Item Number: " Item.ItemNum " has been generated." - VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. - *******/ - END. /* If avail suplier*/ - - END. /*if avail supplieritemxref*/ - -END. diff --git a/SportsApp/Sports/AppServer/sports2020trgs/wrord.p b/SportsApp/Sports/AppServer/sports2020trgs/wrord.p deleted file mode 100644 index ad36418..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/wrord.p +++ /dev/null @@ -1,11 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: wrord.p -** Descript: -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER Procedure FOR Write of Order. - diff --git a/SportsApp/Sports/AppServer/sports2020trgs/wrordl.p b/SportsApp/Sports/AppServer/sports2020trgs/wrordl.p deleted file mode 100644 index 0263bf1..0000000 --- a/SportsApp/Sports/AppServer/sports2020trgs/wrordl.p +++ /dev/null @@ -1,32 +0,0 @@ -/***************************************************************************\ -***************************************************************************** -** -** Program: wrordl.p -** Descript: OrderLine write trigger. -** -***************************************************************************** -\***************************************************************************/ - -TRIGGER PROCEDURE FOR Write OF OrderLine. - -/* Automatically calculate the Extended Price based on Price, Qty, Discount */ - -ExtendedPrice = Price * Qty * (1 - (Discount / 100)). - - -/* In some applications you may want to prohibit the change of a record - * based on the value of some field(s) in that record. This is an example - * of what you might do if you want to prevent the change of an order-line - * record if a ship-date has already been entered for that record. - */ -/* -FIND FIRST order OF orderline NO-ERROR. -IF AVAILABLE order THEN DO: - IF order.shipdate <> ? THEN DO: - MESSAGE "Cannot change an order's detail information when a ship" - "date has been entered for that order." - VIEW-AS ALERT-BOX INFORMATION BUTTONS OK. - RETURN ERROR. - END. -END. -*/ diff --git a/SportsApp/Sports/AppServer/state.i b/SportsApp/Sports/AppServer/state.i deleted file mode 100644 index 923c166..0000000 --- a/SportsApp/Sports/AppServer/state.i +++ /dev/null @@ -1,29 +0,0 @@ - - /*------------------------------------------------------------------------ - File : State - Purpose : - Syntax : - Description : - Author(s) : Administrator - Created : Fri Jul 27 14:14:58 EDT 2018 - Notes : - ----------------------------------------------------------------------*/ - - /* *************************** Definitions ************************** */ - - /* ******************** Preprocessor Definitions ******************** */ - - /* *************************** Main Block *************************** */ - - /** Dynamically generated schema file **/ - -@openapi.openedge.entity.primarykey (fields="State"). - -DEFINE TEMP-TABLE ttState BEFORE-TABLE bttState -FIELD State AS CHARACTER LABEL "State" -FIELD StateName AS CHARACTER LABEL "State Name" -FIELD Region AS CHARACTER LABEL "Region" -INDEX State IS PRIMARY UNIQUE State ASCENDING . - - -DEFINE DATASET dsState FOR ttState. \ No newline at end of file diff --git a/SportsApp/Sports/Dockerfile b/SportsApp/Sports/Dockerfile deleted file mode 100644 index c2410da..0000000 --- a/SportsApp/Sports/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM alpine:3.8 - -# Root location -ARG ROOT_FOLDER=/deploy-staging - -ARG MANIFEST_VERSION=1.0 - -# Copy archive file -COPY ./ablapps/ ${ROOT_FOLDER}/artifacts/ablapps - -# Create "META-INF/MANIFEST.MF" file -RUN mkdir ${ROOT_FOLDER}/META-INF -RUN echo "Manifest-Version: ${MANIFEST_VERSION}" > ${ROOT_FOLDER}/META-INF/MANIFEST.MF -RUN echo "Date-Timestamp: `date +'%Y-%m-%dT%H:%M:%S%z'`" >> ${ROOT_FOLDER}/META-INF/MANIFEST.MF - -# Set working directory -WORKDIR ${ROOT_FOLDER} diff --git a/SportsApp/Sports/PASOEContent/META-INF/MANIFEST.MF b/SportsApp/Sports/PASOEContent/META-INF/MANIFEST.MF deleted file mode 100644 index da0b623..0000000 --- a/SportsApp/Sports/PASOEContent/META-INF/MANIFEST.MF +++ /dev/null @@ -1,8 +0,0 @@ -Manifest-Version: 1.0 -Implementation-Version: 12.8.0 -Implementation-Vendor: Progress Software -Implementation-URL: http://www.progress.com -Build-Timestamp: 2023-05-08T07:18:48.723-0400 -Implementation-Title: PAS for OpenEdge ABL Application -ABL-web-application-version: 0.0.0.0 - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/README b/SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/README deleted file mode 100644 index 9d43bf2..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/README +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright © 1998-2019 Progress Software Corporation and/or one of its subsidiaries or affiliates. All rights reserved. - -This README describes contents of the parent folder "rest". The contents -of parent "rest" folder is used by the runtime to initialize REST transport -for the oeabl service. The layout of the rest folder containing a deployed -service e.g. .paar will look like: - -/rest -| ----- -| | -| ---- .paar -| | -| ---- stagingDir -| | -| ---- Service1.restoe -| | -| ---- mapping.xml -| | -| ---- resourceModel.xml -| | -| ---- spring.xml -| ----- -| . -| . -| . ----- runtime.props - -Each new deployed service will have the same layout as explained for Service1 here. - -1) - Directory - For each Service (as represented by .paar), new directory is created. The name - of the directory bears the name of the Service. This directory contains the - (.paar) and another directory named "stagingDir" which is the inflated version - of the (.paar).The "stagingDir" is created when web application context is initialized - during Tomcat startup. This directory is deleted when the web application context - is destroyed during the Tomcat shutdown. - -2) stagingDir/.paar [Progress Archive] - Archive file containing all the files required to create the REST service endpoint. - Used by REST runtime to create and manage endpoints. DO NOT DELETE. - -3) stagingDir/.restoe - This file contains metadata for the AppServer procedure and the Input/Output params. - EDIT NOT RECOMMENDED. - -4) stagingDir/mapping.xml - This file contains information regarding the mapping of Input/Output Parameters - of the Appserver procedure with the HTTP Request/Response parameters respectively. - EDIT NOT RECOMMENDED. - -5) stagingDir/resourceModel.xml - This file contains information regarding the mapping of AppServer procedure with - the HTTP Verb and HTTP Path. - EDIT NOT RECOMMENDED. - -6) stagingDir/spring.xml - This file serves as seed of all the adapter functionality. The REST adapter runtime - uses this file to load all the mappings and procedure metadata. - EDIT NOT RECOMMENDED. - -7) runtime.props - Property file containing default values of runtime properties. - Each deployed SOAP service will inherit property values for its own copy - of runtime properties from this file. - - -Deploying a PAAR (Progress Archive) in an OEABL Application for PAS Server. -=========================================================================== - -The PAAR maps the Progress 4GL procedures that run on the Application Server. -Deployment of PAAR can be performed using: - -1) deployREST.sh/deployREST.bat utility: - - - The utility reside in the PAS instance's bin folder i.e. {CATALINA_BASE}/bin. - - - General syntax to deploy a paar is: - Examples: - Deploy test.paar to OEABL WebApp named ROOT - # $CATALINA_BASE/bin/deployREST.sh /tmp/test.paar ROOT - - Undeploy an existing REST service named test from OEABL WebApp ROOT - # $CATALINA_BASE/bin/deployREST.sh test ROOT -undeploy - - - Deployment requires a webapp context reload / server restart. To limit - production downtime, deploy all the required REST services first and then - performing Server restart. - - - Once deployed, a new folder is created in - $CATALINA_BASE/webapps//WEB-INF/adapters/rest/. - After the server restart, and successful load of the service by the runtime - [.xml, .restoe ] are created inside the stagingDir. - - -2) REST webservice call to oemanager webapp: - - - oemanager webapp serves as a central utility to manage OEABL webapps. - - - General syntax to deploy a paar is: - Examples: - Deploy test.paar to OEABL WebApp named ROOT - # curl -X POST --data-binary @test.paar - # http://:/oemanager/applications//webapps/ROOT/transports/rest/oeservices - # -H "Accept: application/vnd.progress+json" - # -H "Content-Type: application/vnd.progress.paar+xml" - # -H "Content-Disposition: attachment; filename=test.paar" -v - - Undeploy an existing REST service named test from OEABL WebApp ROOT - # curl -X DELETE - # http://:/oemanager/applications//webapps/ROOT/transports/rest/oeservices/test - - - Once deployed, a new folder is created in - $CATALINA_BASE/webapps//WEB-INF/adapters/rest/. - After the server restart, and successful load of the service by the runtime - [.xml, .restoe ] are created inside the stagingDir. - - - oemanager automatically loads all the new definitions by performing - a reload of the WebpApp by start/stop of the WebApp context. diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/_oepingService/_oepingService.paar b/SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/_oepingService/_oepingService.paar deleted file mode 100644 index 172cda48fd6f1deed6491ee07b425ab211a8239c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2871 zcmaKucTiL58pi1fO+c!EAT@M>pcH9A7lhCPs}RacfWU?t2t^E`M2bX;0!m;(q$yqL zB@}}Yk&d9$P!$Df(ovKf?%Wx@duQ+S&6#ulIPaV}@B96}=dp$|GV##?fj~Mr=e;X* zzlfdo?2AC7QGT8(H+_AIY++U%s@xF|V~(vw(q7-LXggC4k;Tu`t46hP8})?r-LLSS zwA6?>_5+9{cQYnn?XjV=xY^yx;HeuZygp>v9?fNN51SYTkW37FKi`1bSm$YQ+|42} zQR=9V7cjQ6Ma&lf@w=}nJcP?!g7k?>;IaOWoy~Z)YSU$p8VQ?(tI_YIx#c;>2?CFu zX1tu5#%cy?40b(D9*pM0nPTlBXMtiAioifBbQvY{QJnw!fd80&MrK~*bU~ke0v%1Qv@<(35^88CgsY&7=gFt z{wVElFHxb>+n1OqxRo7O!> zYbXFH%cz@`N>4`@$w)_M{g0#Mp^Y*SiSZ8(bVFMByCZ#028x4`EE(0fSMMJHys*i*wE5eg;|D$%L2mvHQCX;R{E zT;_4RK^U$St}#3Cz~yDhFwqBcHp>p=j{58Ci4z<9SzyjH(Bb6#fba1wcM5nq(^aoY z5u6N}{UJYw)6cePT;`4}N@fKaX`3Mlay)w5LQXe|Alq=cXfszIM60YjikELc*qfXk zAZySRSr=byE2Or>7^XROHw>`!8tm;aEzJ$)9n_)J$qT4@2qU<1#a(F@D`^#@KJc zFy!=9wMBHK!B_~;63C%!q>(VFy+1OxeBfvI;8U!(?Nv4MMz^xDQ=z(Irx*Sh*KDOf zM_TSjAN6Ib0$+5PZ%#T~$PXe>PZwwyV)_-@9()DezxA6%^Adl-BfNi{T)1v%E%>*) z?caNCXJ%*G3T8j1K&>p;5K6rA8`ZX|wim-AO^B|B1WrJ%Wh3dR!Td!K>bNFHKaL}c zxFo2yVx9iSa>&E445+&s?Zi#(uX=3#jM)F{Tm$p(AsoL9NW!)JR` zA$^6q_rj&RWD#X!3HvnBpwmNV^vb+C)78a3nrSx2W?eCRQk;;Yj(pbutvMjX-Q=vV zbYJlA9&mTQopEVS=XZO{jwCStYe#b>{+Z`!8xbAadg%B18H4z@63tA#R-DJ$TQ=zO_R|!oJklgfC z-zY=9$qT?`QpsIMi5}k%lUuxvb#vOa*Jn`nqBj}GC3ez>l-~6lSv42UxcF5iv}$Lq zkZW~C5v}(%A?+=oV5$2rnWUe~m~HXfkZUT(`8WY8S6St&u+lEgQCrP&q;8o8A$1m) zmldQa#j8w#KP)$2X1uCY-+c^W3VfRxUopj%{JawFX*jAqM|}cl#}?4G{v)A?8$D}p z)}iA*=gX&G^tkTL`c!4lqYCL5Dm4afH5#v7Y$;`nNclQX74@iO$2mT_D|c};jneye zuLrcdUSaIr9R@OhA`S;`_5Pq=Z=w8Hx}Yuwaqv@05j%))dU)*{Osj0}H%%Y6t_2e+r9Li*unYDId!?E1;7%djJ+%GHvnVk26Lu(ssgrXh0Oib!dl*7-@% z=$vxZmAf_c$uVT{l0acbGU%SEusTv!+8nLZ#D=B6 zP+w>kX#VXpX{#Z+KHV~?>ybR!?MZ^oUE*n<#mt;i8_R ziqo2lmT0o>f~zimlhnb`gb3KwYQ63SH}qVvXv3S_%TtY(Qr)Y6R~q$3ckuhyQ{o$s zc^qw-_>Q1Qc6*eFBu_P4fopcAqj}2J#WkU_>ooTaFVWm#fRAJxey<*yz#rz48C4RI zl3tbQz`+&aNyt?cP*Bb_X${1XB!nf7ucWgvb&70W^T8>NMOnb}Rq$Hvq(Lv``5-TeDcn@iObg+DqyuN9se>CVqF!d5wWPh6SYaD7^H?UmKCH*?oTjo4Cz z{@Y};o~D|p?B0eEp~DnjV;2ByNueK^*l8@Z;4xnxQ+3YA4dOAU0!@w2H$|4OBFL{| zK_K%H8Q*!|J)_<~$*T|d+)RvygFHbno3RopT%WQ%g;yPn8RZC&%a~C3P?4U=dbyA& z6T&J~r^5*PYU@#Me#mu)l%GooST*kBEH8?K6Tnz@@3`O<$#o45YTfvpl;_~WwQi>! zt6P*uQ{6hpVCm9u$y&kOCLTVefbSBH9)9*ME?GrSV3htKK^gdTkN*vC?AV5OkTd$P zh`?dBQs7Pi=DFjUW5M>xC*n0_i%U%r-;r)!s#`BT34GvlN~Z3AP+>O|FX&r>=#FOs z`;}V?O-;zrnplmVwXkf%8eNT9QT$(@5JvOU#T~motM&<>3#v#5!!XPJq6Um17fT*| z5pdhlcdIpiy$$|nq+*C&MBO^u=#QtIERhT+oKW|k34dUqqZ4F3;e?)nkM3t){55`@ zfA3$MRsu=@ diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props b/SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props deleted file mode 100644 index 0f3786f..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - 1 - - 4 - 0 - 1 - 0 - 1 - 0 - -1 - 1 - 2 - 1 - 0 - 0 - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/README b/SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/README deleted file mode 100644 index b44400d..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/README +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright © 1998-2019 Progress Software Corporation and/or one of its subsidiaries or affiliates. All rights reserved. - -This README describes contents of the parent folder "soap". The contents -of parent "soap" folder is used by the runtime to initialize SOAP transport -for the oeabl service. The layout of the rest folder containing a deployed -service e.g. .paar will look like: - -/soap -| ----- -| | -| ---- .soapoe -| | -| ---- .wsdl -| ----- -| . -| . -| . ----- runtime.props -| . ----- camel-spring.xml - -Deploying a WSM (Web Service Mapping) in an OEABL Appliction for PAS Server. -=========================================================================== - -The WSM maps the Progress 4GL procedures that run on the Application Server. -Deployment of WSM can be performed using: - -1) deploySOAP.sh/deploySOAP.bat utility: - - - The utility reside in the PAS instance's bin folder i.e. {CATALINA_BASE}/bin. - - - General syntax to deploy a wsm is: - Examples: - Deploy test.wsm to OEABL WebApp named ROOT - # $CATALINA_BASE/bin/deploySOAP.sh /tmp/test.wsm ROOT - - Undeploy an existing SOAP service named test from OEABL WebApp ROOT - # $CATALINA_BASE/bin/deploySOAP.sh test ROOT -undeploy - - - While using the utility ensure that the SOAP transport is ENABLED for the - target OEABL Service - - - Once deployed, the above mentioned files [.props, .wsad, .wsdl] will be - created in the $CATALINA_BASE/webapps//WEB-INF/adapters/soap - - -2) REST webservice call to oemanager webapp: - - - oemanager webapp serves as a central utility to manage OEABL webapps. - - - General syntax to deploy a wsm is: - Examples: ( using curl client. You may wish to use any HTTP client ) - Deploy test.wsm to OEABL WebApp named ROOT - # curl -X POST --data-binary @test.wsm - # http://:/oemanager/applications//webapps/ROOT/transports/soap/oeservices - # -H "Accept: application/vnd.progress+json" - # -H "Content-Type: application/vnd.progress.wsm+xml" - # -H "Content-Disposition: attachment; filename=test.wsm" -v - - Undeploy an existing SOAP service named test from OEABL WebApp ROOT - # curl -X POST - # http://:/oemanager/applications//webapps/ROOT/transports/soap/oeservices/test - - - While using the utility ensure that the SOAP transport is ENABLED for the - target OEABL Service - - - Once deployed, the above mentioned files [.soapoe, .wsdl] will be - created in the $CATALINA_BASE/webapps//WEB-INF/adapters/soap/ directory - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml b/SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml deleted file mode 100644 index 4253de9..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - com.progress.appserv.adapters.camel.soap.CamelSOAPException - true - - - - - - - - - - \ No newline at end of file diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/web/README.txt b/SportsApp/Sports/PASOEContent/WEB-INF/adapters/web/README.txt deleted file mode 100644 index adf83fe..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/web/README.txt +++ /dev/null @@ -1,34 +0,0 @@ -######################################################### - -The "web" folder will have the WebHandler definition for each Service. For example, if we are going -to create a WebHandler for the service named "SportsSvc" then a folder named "SportsSvc" should -be created and inside that we should place the webhandlers mapping file. - - -The handler configuration should be a JSON Object where each handler definition is a string -inside the JSON Object with the below format. - - "uri":"","class":"handler class name" - - -For ROOT WebApp, ROOT.handlers should be placed inside the "ROOT" folder with no service name -in the handlers file. - -Here is an example of a handlers file - -{ - "version": "2.0", - "serviceName": "PingService", - "handlers": [ - { - "uri":"/_oeping","class":"OpenEdge.Web.PingWebHandler" - "uri":"/pdo/{service}","class":"OpenEdge.Web.DataObject.DataObjectHandler" - } - ] -} - -The URL to access the service should be in the format of /web// -So, for the above example it would be - /web/PingService/_oeping - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml deleted file mode 100644 index fdfd2d5..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-none.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-none.xml deleted file mode 100644 index 7374281..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-none.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml deleted file mode 100644 index 8877714..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml +++ /dev/null @@ -1,218 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml deleted file mode 100644 index 9809824..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml +++ /dev/null @@ -1,450 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml deleted file mode 100644 index 5e83e9a..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml +++ /dev/null @@ -1,346 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml deleted file mode 100644 index 2134c8b..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml +++ /dev/null @@ -1,332 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml deleted file mode 100644 index 8b852ad..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml +++ /dev/null @@ -1,411 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml deleted file mode 100644 index 895c4c5..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml +++ /dev/null @@ -1,496 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - WEB-INF/meta-psc-oerestadapter-idp-test.xml - - - - - - - - - - - - - - - - WEB-INF/metadata_-_RestSP.xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml deleted file mode 100644 index 369b7f0..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml +++ /dev/null @@ -1,305 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml deleted file mode 100644 index 9a7d03f..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml +++ /dev/null @@ -1,501 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml deleted file mode 100644 index 2502adb..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml +++ /dev/null @@ -1,403 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml deleted file mode 100644 index 9cda4c6..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml +++ /dev/null @@ -1,385 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml deleted file mode 100644 index ead3026..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml +++ /dev/null @@ -1,455 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml deleted file mode 100644 index 8a6d4b6..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml +++ /dev/null @@ -1,541 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - WEB-INF/meta-psc-oerestadapter-idp-test.xml - - - - - - - - - - - - - - - - WEB-INF/metadata_-_RestSP.xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-basic.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-basic.xml deleted file mode 100644 index 9c3bc87..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-basic.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-none.xml b/SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-none.xml deleted file mode 100644 index 51419a9..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-none.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/classes/manifest.txt b/SportsApp/Sports/PASOEContent/WEB-INF/classes/manifest.txt deleted file mode 100644 index e69de29..0000000 diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/home/ServerStatus.html b/SportsApp/Sports/PASOEContent/WEB-INF/home/ServerStatus.html deleted file mode 100644 index 02f5524..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/home/ServerStatus.html +++ /dev/null @@ -1,132 +0,0 @@ - - - - - -Progress Application Server for OpenEdge - - - - - - - - - - - - -
-
- - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp deleted file mode 100644 index 6889450..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp +++ /dev/null @@ -1,54 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - isErrorPage="true" - trimDirectiveWhitespaces="true" - errorPage="/WEB-INF/jsp/exceptionPage.jsp" - import="java.io.*" - import="java.util.*" - import="java.system.*" %> - -<%-- Java scriptlet to cleanup raw input properties and attributes used as - psc.as.attr.xxxxx tokens by the HTML template found in this file. --%> - -<%-- Import OWASP --%> -<%@ page import="org.owasp.encoder.Encode" %> - -<%-- Begin editable JSON response template: --%> - -<%@ include file="loadErrorData.jsp" %>{ -"error_code": <%=request.getAttribute("psc.as.attr.errorCode")%> -, "status_txt": "<%=Encode.forHtml((String)request.getAttribute("psc.as.attr.errorMessage"))%>" - -<% if ( (Integer)request.getAttribute("psc.as.attr.detailLevel") > 1) { %> <%-- Add verbose information here --%> -, "error_details": { - "remote_user": "<%=request.getRemoteUser()%>" -, "user_principal": "<%=request.getUserPrincipal()%>" -, "url_scheme": "<%=request.getScheme()%>" -, "remote_addr": "<%=request.getRemoteAddr()%>" -, "server_name": "<%=request.getServerName()%>" -, "product_type": "<%=request.getAttribute("psc.as.attr.product")%>" -, "http_status": <%=(Integer)request.getAttribute("javax.servlet.error.status_code")%> -, "error_detail": "<%=request.getAttribute("psc.as.attr.errorDetail")%>" -} -<% }; %> <%-- End of Error Details --%> - -<% if ( (Integer)request.getAttribute("psc.as.attr.detailLevel") > 2) { %> <%-- Add more debug information here --%> -, "debug_details": { - "http_method": "<%=request.getMethod()%>" -, "web_application": "<%=request.getAttribute("psc.as.attr.webApp")%>" -, "transport": "<%=request.getAttribute("psc.as.attr.transport")%>" -, "request_url": "<%=request.getAttribute("psc.as.attr.requrl")%>" -, "path_info": "<%= Encode.forHtml((String)request.getPathInfo())%>" -, "servlet": "<%=(String)request.getAttribute("javax.servlet.error.servlet_name")%>" -, "uri": "<%=(String)request.getAttribute("javax.servlet.error.request_uri")%>" -, "exception_class": "<%=request.getAttribute("psc.as.attr.exceptionName")%>" -, "exception_message": "<%=request.getAttribute("psc.as.attr.exceptionMessage")%>" -, "exception_stack_trace": "<%=request.getAttribute("psc.as.attr.exceptionStack")%>" -} -<% }; %> - -} -<%-- End editable JSON response template: --%> - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp deleted file mode 100644 index 0cc6ea2..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp +++ /dev/null @@ -1,39 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - isErrorPage="true" - trimDirectiveWhitespaces="true" - import="java.io.*" - import="java.util.*" - import="java.system.*" %> -<%@ taglib prefix="sec" uri="/WEB-INF/security.tld" %> -<%@ page import="org.slf4j.Logger,org.slf4j.LoggerFactory" %> -<%! static final Logger logger = LoggerFactory.getLogger("errorPage.jsp"); %> -<% logger.error("Exception in jsp for uri: {}", pageContext.getErrorData().getRequestURI(),pageContext.getException()); %> -<% - String acceptHeader = request.getHeader("accept"); - if ( acceptHeader != null && acceptHeader.matches("^(.*,|)application/(|[\\w\\.]+\\+)json($|,.*|;.*)") ) { - request.setAttribute("psc.as.attr.errorFormat", "json"); - response.setContentType("application/json;charset=UTF-8"); -%> -<%@ include file="errorJSONBody.jsp" %> -<% - } else { - request.setAttribute("psc.as.attr.errorFormat", "html"); -%> - - -Progress Application Server Error - - - - -<%@ include file="errorPageHeader.jsp" %> -<%@ include file="errorPageBody.jsp" %> -<%@ include file="errorPageFooter.jsp" %> - - -<% - } -%> diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp deleted file mode 100644 index 6ce003c..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp +++ /dev/null @@ -1,54 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - isErrorPage="true" - trimDirectiveWhitespaces="true" - errorPage="/WEB-INF/jsp/exceptionPage.jsp" - import="java.io.*" - import="java.util.*" - import="java.system.*" %> - <%-- Java scriptlet to cleanup raw input properties and attributes used as - psc.as.attr.xxxxx tokens by the HTML template found in this file. --%> -<%@ include file="loadErrorData.jsp" %> - -<%-- Import OWASP --%> -<%@ page import="org.owasp.encoder.Encode" %> - -<%-- Begin Editable HTML page template: --%> -

<%=Encode.forHtml((String)request.getAttribute("psc.as.attr.errorMessage"))%> - -<% if ( (Integer)request.getAttribute("psc.as.attr.detailLevel") > 1) { %> <%-- Add verbose information here --%> -

- - - - - - - - - - -
Error details
Remote user: <%=request.getRemoteUser()%>
User principal: <%=request.getUserPrincipal()%>
Scheme: <%=request.getScheme()%>
Remote address: <%=request.getRemoteAddr()%>
Server name: <%=request.getServerName()%>
PASOE product type: <%=request.getAttribute("psc.as.attr.product")%>
HTTP status: <%=(Integer)request.getAttribute("javax.servlet.error.status_code")%>
Error detail: <%=request.getAttribute("psc.as.attr.errorDetail")%>
-<% }; %> <%-- End of Error Details --%> - -<% if ( (Integer)request.getAttribute("psc.as.attr.detailLevel") > 2) { %> <%-- Add more debug information here --%> - - - - - - - - - - - - - -

Debug details
HTTP method: <%= request.getMethod()%>
Web application: <%=request.getAttribute("psc.as.attr.webApp")%>
Transport: <%=request.getAttribute("psc.as.attr.transport")%>
Request URL: <%=request.getAttribute("psc.as.attr.requrl")%>
Path info: <%= Encode.forHtml((String)request.getPathInfo())%>
Servlet name: <%=(String)request.getAttribute("javax.servlet.error.servlet_name")%>
URI: <%=(String)request.getAttribute("javax.servlet.error.request_uri")%>
Exception class: <%=request.getAttribute("psc.as.attr.exceptionName")%>
Exception message: <%=request.getAttribute("psc.as.attr.exceptionMessage")%>
Exception stack trace: <%=request.getAttribute("psc.as.attr.exceptionStack")%>
-<% }; %> - -<%-- End Editable HTML page template: --%> - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp deleted file mode 100644 index fc57c3a..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp +++ /dev/null @@ -1,2 +0,0 @@ -


-<%@ include file="/static/commonPageFooter.html" %> diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp deleted file mode 100644 index 93d8774..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp +++ /dev/null @@ -1,3 +0,0 @@ -<%@ include file="/static/commonPageHeader.html" %> -
An error occurred while executing your request!
-
diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp deleted file mode 100644 index 1d2f19a..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp +++ /dev/null @@ -1,38 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - trimDirectiveWhitespaces="true" - isErrorPage="true" - import="java.io.*" - import="java.util.*" - import="java.system.*" %> - <%! String pageType = "exception"; %> -<% - String acceptHeader = request.getHeader("accept"); - if ( acceptHeader != null && acceptHeader.matches("^(.*,|)application/(|[\\w\\.]+\\+)json($|,.*)") ) { - request.setAttribute("psc.as.attr.errorFormat", "json"); - response.setContentType("application/json;charset=UTF-8"); -%> -<%@ include file="errorJSONBody.jsp" %> -<% - } else { - request.setAttribute("psc.as.attr.errorFormat", "html"); -%> - - -Progress Application Server Exception - - - - -<%@ include file="exceptionPageHeader.jsp" %> -<%@ include file="errorPageBody.jsp" %> -<%@ include file="exceptionPageFooter.jsp" %> - - -<% - } -%> - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp deleted file mode 100644 index fc57c3a..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp +++ /dev/null @@ -1,2 +0,0 @@ -

-<%@ include file="/static/commonPageFooter.html" %> diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp deleted file mode 100644 index 5d0e952..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp +++ /dev/null @@ -1,3 +0,0 @@ -<%@ include file="/static/commonPageHeader.html" %> -
An application exception occurred while executing your request! -
diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties deleted file mode 100644 index be01107..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties +++ /dev/null @@ -1,25 +0,0 @@ -code0=Unknown HTTP status code -code400=Bad Request -code401=Unauthorized -code402=reserved -code403=Forbidden -code404=Not Found -code405=Method Not Allowed -code406=Not Acceptable -code407=Proxy Authentication Required -code408=Request Timeout -code409=Conflict -code410=Gone -code411=Length Required -code412=Precondition Failed -code413=Request Entity Too Large -code414=Request-URI Too Long -code415=Unsupported Media Type -code416=Requested Range Not Satisfied -code417=Expectation Failed -code500=Server Error -code501=Not Implemented -code502=Bad Gateway -code503=Service Unavailable -code504=Gateway Timeout -code505=HTTP version Not Supported diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties deleted file mode 100644 index 0407f9b..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties +++ /dev/null @@ -1,25 +0,0 @@ -code0=Unknown HTTP status code -code400=Bad Request: malformed client HTTP request -code401=Unauthorized: authentication failed due to bad credentials -code402=reserved -code403=Forbidden: the server refuses to fulfill the client's request -code404=Not Found: the client requested URI was not found -code405=Method Not Allowed: the method type was not allowed for requested URI -code406=Not Acceptable: the server could not return an acceptable content type -code407=Proxy Authentication Required: client authentication to the proxy server is required -code408=Request Timeout: the client did not send a request in the configured amount of time -code409=Conflict: the server found a conflict in the resource's state -code410=Gone: the requested URI was moved and has no forwarding URL -code411=Length Required: the server requires the HTTP request to supply a Content-length -code412=Precondition Failed: a HTTP request precondition could not be satisfied -code413=Request Entity Too Large: the client request is larger than the server allows -code414=Request-URI Too Long: the client request URI is larger than the server allows -code415=Unsupported Media Type: the client request media-type is not supported -code416=Requested Range Not Satisfied: a client request range header is missing or invalid -code417=Expectation Failed: a client request Expect header cannot be satisfied -code500=Server Error: the server could not produce a response entity due to an internal error -code501=Not Implemented: the server lacks sufficient functionality to execute the request -code502=Bad Gateway: a proxy server detected an invalid response from the the upstream server -code503=Service Unavailable: the requested service is temporarily offline -code504=Gateway Timeout: a proxy server did not recieve a response in the configured amount of time -code505=HTTP version Not Supported: the client's request specified an unsupported HTTP version diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp deleted file mode 100644 index 39a2ecf..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp +++ /dev/null @@ -1,241 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - isErrorPage="true" - trimDirectiveWhitespaces="true" - errorPage="/WEB-INF/jsp/exceptionPage.jsp" - import="java.io.*" - import="java.util.*" - import="java.system.*" %> - <%-- Java scriptlet to cleanup raw input properties and attributes used as tokens - by the HTML template found at the end of this file. --%> - <% - // Generate the dynamic body content - try { - // Detail level: 0 Use product type defaults - // The following values 1 - 4 override the - // default product type values - // 1 Terse (least information) - // 2 verbose (more information) - // 3 debug (add more jsp page debug info) - String errorMessage = ""; - int detailLevel = 0; - String product = "dev"; - String requrl = "unknown"; - String webAppName = "/ROOT"; - int errorCode = 0; - String errcodeStr = "0"; - String transport = "unknown"; - Throwable appException = null; - String exceptionName = ""; - String exceptionMessage = ""; - String exceptionStack = ""; - String errorDetail = ""; - String statusDetail = ""; - String errorFormat = (String)request.getAttribute("psc.as.attr.errorFormat"); - - // Load the development/production setting for the initial - // detailLevel. - product = System.getProperty("psc.as.security.model"); - if (product == null ) { - product = "default"; - } else { - if ( product.matches("^dev.*") ) { - product = "dev"; - detailLevel = 3; - } else if ( product.matches("^prod.*") ) { - product = "prod"; - detailLevel = 1; - } else { - product = "default"; - detailLevel = 2; - } - } - - // Load any specific detailLevel from the web application env - try { - String sdetailLevel = application.getInitParameter("detailLevel"); - if ( sdetailLevel != null ) { - int cfgDetailLevel = Integer.parseInt(sdetailLevel); - if ( cfgDetailLevel > 0 ) { - detailLevel = cfgDetailLevel; - } - } - } catch(Throwable th) { - } - - // Cleanup the web application name, for the root app a blank - // is returned, but we want to pass in ROOT in that case. - String cp = request.getContextPath(); - if ( cp != null && - cp.length() > 0) { - webAppName = cp; - } - - // Bundle together the error information for writing to the page - if(pageContext != null) { - ErrorData errctx = null; - - // Get the implicit error data object for this request - try { - errctx = pageContext.getErrorData(); - } catch(NullPointerException ne) { - - // Sometimes this call causes a NullPointerException (PageContext.java:514) - // Catch and ignore it.. it effectively means we can't use the ErrorData - } - - // Prepare error report - if(errctx != null) { - // Unload the basic error object fields - requrl = errctx.getRequestURI(); - errorCode = errctx.getStatusCode(); - transport = errctx.getServletName(); - appException = errctx.getThrowable(); - - errorDetail = (String)request.getAttribute("javax.servlet.error.message"); - - // Cleanup some null values with defaults - if ( requrl == null ) requrl = "/"; - if (transport == null ) transport = "default"; - - // The building of the error information is divided into - // HTTP status codes and Exceptions - if ( appException != null ) { - StringBuilder sb = new StringBuilder(); - // Handle application excpetions here - if(appException.getMessage() != null && - appException.getMessage().indexOf("Exception in JSP") != -1) { - sb = new StringBuilder("An error occurred in a JSP file ...\n\n
" + 
-                                    appException.getMessage() + "
"); - } else { - String ecls = ""; - if ( detailLevel > 1 ) { - ecls = appException.getClass().getName() + " ; "; - } - sb = new StringBuilder( ecls + appException.getMessage()); - } - errorDetail = sb.toString().trim(); - - //Cull package name from exception class to avoid revealing structure of program - if(detailLevel < 3) - while(errorDetail.indexOf('.') != -1) - errorDetail = errorDetail.substring(errorDetail.indexOf('.')+1); - - if ( detailLevel > 2 ) { - sb.setLength(0); - StackTraceElement stea[] = appException.getStackTrace(); - if ( errorFormat == null || !errorFormat.equals("json") ) { - // Format for HTML - for (StackTraceElement ste : stea ) { - sb.append("
at "); - sb.append(ste.toString()); - } - } else { - // Format for JSON - String fsep = ""; - sb.append("[ \n"); - for (StackTraceElement ste : stea ) { - sb.append(fsep); - sb.append("\""); - sb.append(ste.toString()); - sb.append("\"\n"); - fsep = ","; - } - sb.append("\n]\n"); - } - exceptionStack = sb.toString().trim(); - } - // Save off individual exception fields rather than needing to - // access the exception built-in object - exceptionName = appException.getClass().getName(); - exceptionMessage = appException.getMessage(); - } - - // Now load error description based on the status code and - // write it to the page - if ( errorCode == 0 ) { - // Sometimes the error object does not include the - // status code ( returns zero ), so try the - // built-in servlet attributes - errcodeStr = (String)request.getAttribute("javax.servlet.error.status_code"); - if ( errcodeStr != null ) { - errorCode = Integer.parseInt(errcodeStr); - } else { - String pageErrorCode = request.getParameter("statusCode"); - if ( pageErrorCode != null ) { - errorCode = Integer.parseInt(pageErrorCode); - } - } - } - - // Load the static HTTP status code description - // Properties file lookup key... - errcodeStr = "code" + Integer.toString(errorCode); - - try { - String descFile = "descFile"; - // Determine which static text file to load from - if (detailLevel < 2 ) { - descFile = "/WEB-INF/jsp/httpCodeDesc-terse.properties"; - } else { - descFile = "/WEB-INF/jsp/httpCodeDesc-verbose.properties"; - } - // Open properties file and load the static - // error code text - InputStream stream = - application.getResourceAsStream(descFile); - Properties props = new Properties(); - props.load(stream); - statusDetail = props.getProperty(errcodeStr); - // if the error code text is not found, then get - // the default error code zero (0) - if ( statusDetail == null ) { - String tmpDetail = props.getProperty("code0"); - if ( tmpDetail == null ) { - tmpDetail = "undefined status code "; - } - statusDetail = tmpDetail + errorCode; - } - } catch ( Throwable thr ) { - statusDetail = "undefined status code description"; - application.log("HTTP status code descriptions not available: " + - thr.toString()); - } - - // Adjust the error message line according to the amount of detail needed - if (detailLevel < 2 ) { - request.setAttribute( "psc.as.attr.errorMessage", statusDetail.trim() ); - } else { - request.setAttribute( "psc.as.attr.errorMessage", errorCode + " - " + - statusDetail.trim() + " - " + request.getMethod() + " " + requrl); - } - - // Set all the other attributes that are accessible via JSP tags - request.setAttribute("psc.as.attr.detailLevel", detailLevel); - request.setAttribute("psc.as.attr.product", product); - request.setAttribute("psc.as.attr.requrl", requrl); - request.setAttribute("psc.as.attr.errorCode", errorCode); - request.setAttribute("psc.as.attr.errorDetail", errorDetail); - request.setAttribute("psc.as.attr.errcodeStr", errcodeStr); - request.setAttribute("psc.as.attr.transport" , transport); - request.setAttribute("psc.as.attr.webApp" , webAppName); - request.setAttribute("psc.as.attr.exceptionName", exceptionName); - request.setAttribute("psc.as.attr.exceptionMessage", exceptionMessage ); - request.setAttribute("psc.as.attr.exceptionStack", exceptionStack ); - - } else { - application.log( "Internal page generation error - no error context available"); - } - } else { - application.log( "Internal page generation error - no request context available"); - } - } catch(Throwable e2) { - - // Error in error handler - application.log("An internal error has occurred in the error page.\n\n"); - application.log("Please copy the following details and provide it to technical send support.\n"); - application.log(e2.toString()); - } -%> diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp deleted file mode 100644 index e659569..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp +++ /dev/null @@ -1,37 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - errorPage="/WEB-INF/jsp/errorPage.jsp"%> - - -Progress Application Server Login - - - -<%@ include file="/static/commonPageHeader.html" %> -
- Progress Application Server user login: -
-<% - String lmodel = (String)application.getAttribute("oeablLoginModel"); - if ( lmodel.matches("form") ) { -%> -
-
-
-
- -
-<% - } else { -%> -
- Form login is not compatible with the security policy configuration -<% - } -%> -

- <%@ include file="/static/commonPageFooter.html" %> - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp b/SportsApp/Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp deleted file mode 100644 index a392954..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp +++ /dev/null @@ -1,40 +0,0 @@ -<%@ page language="java" contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" session="false" - errorPage="/WEB-INF/jsp/errorPage.jsp"%> - - -Progress Application Server logout - - - - <%@ include file="/static/commonPageHeader.html"%> -

- - - - - -
RemoteUser: <%=request.getRemoteUser()%>
Session: <%=request.getSession(false).getId()%>
RemoteAddr: <%=request.getRemoteAddr()%>
ServerName: <%=request.getServerName()%>
-<% - String lmodel = (String)application.getAttribute("oeablLoginModel"); - if ( lmodel.matches("form") ) { -%> -

-
Do you want to logout this user? -

- -
-<% - } else { -%> -

- Form login is not compatible with the security policy configuration -<% - } -%> -

- <%@ include file="/static/commonPageFooter.html"%> - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/logging.xml b/SportsApp/Sports/PASOEContent/WEB-INF/logging.xml deleted file mode 100644 index 506a410..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/logging.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/metadata/README b/SportsApp/Sports/PASOEContent/WEB-INF/metadata/README deleted file mode 100644 index 0609a03..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/metadata/README +++ /dev/null @@ -1,13 +0,0 @@ -This directory is for SAML Service provider & Identity Provider Metadata files. -No other things should/can be deployed in this directory. - -SAML metadata is an XML document which contains information necessary for interaction with SAML -enabled identity or service providers. -The document contains e.g. URLs of endpoints, information about supported bindings, identifiers and public keys. -Typically one metadata document will be generated for your own service provider and sent to all identity providers you want to enable single sign-on with. -Similarly, each identity provider will make its own metadata available for you to import into your service provider application. - -*Service Provider metadata file can be create by direct modification or you can make your application to generate service provider metadata file for you. -(As PAS is not direct login to IDP, so that you could use same SP metadata file that you have created for the PAS client, which configured with - SAML to handle redirect & direct login with IDP) - -*Identity Provider metadata file can be downloaded from configured IdP and put inside your service provider to validate incoming SAML asseration. \ No newline at end of file diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.csv b/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.csv deleted file mode 100644 index c16d20e..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.csv +++ /dev/null @@ -1,59 +0,0 @@ -# "url-pattern","","" -############## Intercept-url definitions for the APSV transport URIs ############### -"/apsv/**","HEAD","hasAnyRole('ROLE_PSCUser')" -"/apsv/**","GET","hasAnyRole('ROLE_PSCUser')" -"/apsv/**","POST","hasAnyRole('ROLE_PSCUser')" -"/apsv/**","OPTIONS","hasAnyRole('ROLE_PSCUser')" -############## APSV Container Login model Configuration ############### -#"/apsv/**","OPTIONS","hasAnyRole('ROLE_PSCUser','ROLE_ANONYMOUS')" - -############## Intercept-url definitions for the SOAP transport URIs ############### -"/soap/wsdl/**","GET","hasAnyRole('ROLE_PSCUser')" -"/soap/**","POST","hasAnyRole('ROLE_PSCUser')" - -############## Intercept-url definitions for the REST transport URIs ############### -"/rest/**","*","hasAnyRole('ROLE_PSCUser')" - -############## Intercept-url definitions for the WEB transport URIs ############### -"/web/**","*","hasAnyRole('ROLE_PSCUser')" - -############## Intercept-url definitions for the default URI space ################# -"/static/home.html","GET","hasAnyRole('ROLE_PSCUser')" - -"/static/ServerStatus.html","GET","hasAnyRole('ROLE_PSCUser','ROLE_PSCAdmin','ROLE_PSCDebug')" - -"/server/**","GET","hasAnyRole('ROLE_PSCAdmin','ROLE_PSCDebug')" - -"/*","GET","permitAll()" - -"/static/*","GET","permitAll()" - -"/static/error/*","GET","permitAll()" - -"/static/images/*","GET","permitAll()" - -"/static/auth/*","GET","permitAll()" - -"/static/**","GET","hasAnyRole('ROLE_PSCUser')" - -"/**/*.htm*","GET","hasAnyRole('ROLE_PSCUser')" - -"/**/*.gif","GET","hasAnyRole('ROLE_PSCUser')" - -"/**/*.jpg","GET","hasAnyRole('ROLE_PSCUser')" - -"/**/*.css","GET","hasAnyRole('ROLE_PSCUser')" - -"/**/*.js","GET","hasAnyRole('ROLE_PSCUser')" - -"/**/*.json","GET","hasAnyRole('ROLE_PSCUser')" - -"/**/*.asp","GET","hasAnyRole('ROLE_PSCUser')" - -"/**/*.inc","GET","hasAnyRole('ROLE_PSCUser')" - -# Best practice - deny anything not explicitly granted -"/**","*","denyAll()" - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties b/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties deleted file mode 100644 index b490570..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties +++ /dev/null @@ -1,656 +0,0 @@ - ########################################################################## - ## ## - ## Copyright (c) 2018-2023 by Progress Software Corporation ## - ## ## - ## All rights reserved. No part of this program or document may be ## - ## reproduced in any form or by any means without permission in writing ## - ## from Progress Software Corporation. ## - ## ## - ########################################################################## - # - # Spring Security bean properties definition file for a specific oeabl.war - # based web applications found in a PASOE instance - # - # The properties values found in this file constitute a subset of all Spring - # Security configuration properties and their values. Any property value - # declared in this file will supersede a declaration found in any other - # oeablSecurity.properties file in the conf/ or ablapps//conf. - # - # PAS for OE will resolve properties by loading multiple .properties files - # and using the last declared value it finds. The minimum requirement - # is that PAS for OE must find one oeablSecurity.properties file in one - # of the following locations: - # 1) DLC/servers/pasoe/conf/oeablSecurity.properties - # 2) INSTANCE/conf/oeablSecurity.properties - # 3) INSTANCE/ablapps//oeablSecurity.properties - # 4) INSTANCE/webapps//WEB-INF/oeablSecurity.properties <== THIS FILE - # - # The best practice is to declare the properties and values that span - # multiple ABL business applications and their web applications in the - # INSTANCE/conf/oeablSecurity.properties file. - # - # To set a properties and values that apply to all web applications mapped - # to a single ABL business application, create and declare properties in a - # INSTANCE/ablapps//conf/oeablSecurity.properties - # 1) create a directory having the name of the deployed ABL application - # found in the conf/openedge.properties file - # 2) Copy the conf/oeablSecurity.properties into that directory - # 3) Edit the file to contain only the properties that apply to the ABL - # application, leaving the conf/oeablSecurity.properties file to hold - # the defaults - # - # Last, declare the properties and values specific to a web application - # in the INSTANCE/webapps//WEB-INF/oeablSecurity.properties - # - # The web application developer may choose to include the full superset - # of Spring Security properties in their application's - # INSTANCE/ablapps//conf/oeablSecurity.properties - # OR - # INSTANCE/webapps//WEB-INF/oeablSecurity.properties file. - # - # Refer to DLC/servers/pasoe/conf/oeablSecurity.properties for default - # settings. - # - # To use per web application settings, copy the property settings from - # DLC/servers/pasoe/conf/oeablSecurity.properties into this file and set - # the web application specific value. - # - # -------------------- oeablSecurity.properties ------------------------------ - # Detailed descriptions of the properties found in this property file may be - # found in the file: - # - # DLC/servers/pasoe/conf/oeablSecurity.properties.README. - # - ########################################################################## - ## - ## Version: 12.8.0 - ## Date: 2023-05-08 - - ################# Authentication Manager Name list ##################### - ## The following names apply to the authmanager properties in the - ## various transports: - ## http.apsv.authmanager - ## http.soap.authmanager - ## http.rest.authmanager - ## http.web.authmanager - ## http.authmanager - ## - ## Authentication Managers will only apply to loginModels that perform - ## direct logins using user accounts. Therefore, this property is used only - ## for 'basic' and 'form' login models (below) - ## - ## http.all.authmanager will apply the same authentication to all transports. - ## - ## manager name Description - ## =================================================================== - ## local web application WEB-INF/user.properties - ## extlocal web application WEB-INF/user.properties w. - ## encrypted passwords - ## ldap LDAPv3 Directory Service client (simple) - ## ad Microsoft Active Directory Service client - ## oerealm ABL class callback to application accounts - ## sts OpenEdge Authentication Gateway client - ## - ## The http.xxxx.authmanager properties will be ignored for the following - ## client.login.model configurations: - ## oauth2 - ## saml - ## anonymous - ## - http.all.authmanager=local - - ################## The HTTP client Authentication Model to use ########## - ## This property controls which HTTP client authentication model is used - ## between the PASOE client application and the PASOE server application - ## for the . The allowed names are: - ## - ## name Description - ## =================================================================== - ## anonymous No user login - all clients have public access - ## basic Users authenticate using the HTTP BASIC standard - ## form Users authenticate using a HTTP POST message & - ## form data - ## container Users authenticate via Tomcat realm services and - ## authorize URL access via Spring Security - ## sso OpenEdge Single Sign-on using ClientPrincipal - ## access tokens - ## oauth2 OpenEdge support for validating OAuth2 JWT - ## tokens for URL Authorization - ## saml Users authenticate and authorize using SAML token - ## - client.login.model=anonymous - - ################## HTTP BASIC Realm name for All Transports ################## - ## Set the BASIC realm name used by browsers to prompt the user for a - ## user-id/password. - ## - http.all.realm=OpenEdge - - ################## Container (Tomcat) Role mapping properties ################# - ## This property is used by the 'container' Login Model configuration. It - ## provides a [comma separated - no whitespace] list of Role names supplied - ## by the Tomcat realm login token that will be passed through to Spring's - ## URL authorization. - ## - ## Each PAS for OE transport and the default URI space has its own settable - ## list. The property http.jee.all.mappableRoles can be used to set all - ## transports & default at one time. - ## - http.jee.all.mappableRoles=ROLE_PSCUser,ROLE_PSCAdmin,ROLE_PSCOper - - #################APSV Transport Specific Property ####################### - ## For APSV Transport authentication and authorization is disable by default - ## i.e. apsv.security.enable=none. - ## - ## Valid values are: - ## none No HTTP authentication or authorization [default] - ## basic enable HTTP BASIC authentication & role-based authorization - ## container Users authenticate via Tomcat realm services and - ## authorize URL access via Spring Security - ## - apsv.security.enable=none - - #################SOAP Transport Specific property ######################## - ## For SOAP Transport authentication and authorization is disable by default - ## i.e. apsv.security.enable=none. - ## - ## If there is a requirement to enable this in production, then set - ## soap.security.enable property value as "basic". - ## Example: soap.security.enable=basic - ## - ## The http.soap.authprovider property is used to configure the authentication - ## manager as per production need. - ########################################################################## - soap.security.enable=none - - ################# ClientPrincpal creation properties ###################### - ## Properties for the OEClientPrincipalFilter bean - ## This security filter turns an authenticated client's Spring token into an - ## OpenEdge ClientPrincipal object. The filter is thus responsible for: - ## 1. Creating a ClientPrincipal if one was not created in a previous - ## authentication process step - ## 2. If the previous authentication process produced a Spring token - - ## copy that information into the ClientPrincipal - ## 3. If the ClientPrincipal is not already sealed - seal it is using this - ## filter's domain and registry configuration - ## 4. If enablecp is true, then send the ClientPrincipal to the ABL business - ## logic - ## - ## NOTE: The oerealm Authentication Manager configuration does not use these - ## properties. Because of the nature of calling the ABL language, - ## special property handling is required and most of the ClientPrincipal - ## properties are replicated there. - ## - OEClientPrincipalFilter.enabled=true - - ## Location of the encrypted OE Domain Access-codes used to seal Client-Principals - OEClientPrincipalFilter.registryFile=ABLDomainRegistry.keystore - - ## Default OE Domain name to append if not supplied by the user - OEClientPrincipalFilter.domain= - - ## Default ROLE names if none are found for the authenticated user - OEClientPrincipalFilter.roles= - - ## Add authentication process details to Client-Principal - OEClientPrincipalFilter.authz=true - - ## Set Client-Principal token expiration - OEClientPrincipalFilter.expires=0 - - ## Add [any] source user account information to Client-Principal - OEClientPrincipalFilter.accntinfo=false - - ## Generate a Client-Principal for an anonymousUser client - OEClientPrincipalFilter.anonymous=false - - ## Seal a Client-Principal for an anonymousUser client - OEClientPrincipalFilter.sealAnonymous=false - - OEClientPrincipalFilter.appName=OE - - ## Forward an OAuth2 token as a Client-Principal property - OEClientPrincipalFilter.forwardToken=false - - ## Pass an unsealed Client-Principal for an unauthenticated user-id to the - ## ABL application - OEClientPrincipalFilter.passthru=false - - ## Used to obtain an OE Domain name from the authenticated user's list of - ## roles if the user did not supply one (overrides the default domain property) - OEClientPrincipalFilter.domainRoleFilter= - - ## OIDC userinfo properties - OEClientPrincipalFilter.userinfo.servicetype=oidc - OEClientPrincipalFilter.userinfo.url= - - ## LDAP/AD specific: load these user account attributes into the Client-Principal - ## properties - OEClientPrincipalFilter.loadAccntAttrList= - - ## LDAP/AD specific: Validate that the user-id input OE Domain name is valid - ## for the LDAP user account - OEClientPrincipalFilter.validateClientDomain=false - - ## Enable populating Client-Principal user-id attributes - ## with a client's e-mail id instead of their simple user-id and - ## OE Domain name - OEClientPrincipalFilter.useEMailID=false - - ################# CORS security filter properties ######################### - ## Properties for the OECORSFilter Filter bean - ## The security filter that implements the CORS standard for controlling - ## cross site resource access by http clients. - ## - OECORSFilter.responseHeaders=Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,X-CLIENT-CONTEXT-ID - OECORSFilter.allowAll=true - OECORSFilter.allowDomains= - OECORSFilter.allowSubdomains=false - OECORSFilter.allowMethods=GET,POST,PUT,DELETE,OPTIONS,PATCH - OECORSFilter.messageHeaders=Accept,Accept-Language,Content-Language,Content-Type,X-CLIENT-CONTEXT-ID,Origin,Access-Control-Request-Headers,Access-Control-Request-Method,Pragma,Cache-control,Authorization - OECORSFilter.supportCredentials=true - OECORSFilter.maxAge=-1 - - ################# 'sso' Login Model producer/consumer properties ########### - ## Common OpenEdge SSO Producer and Consumer properties - ## (see properties for OESSOTokenManager, OESSOFilter, OESSORefreshFilter ) - OESSO.error.detail=0 - OESSO.require.https=true - - ## Properties for the OESSOTokenManager bean (see authFilters.xml) - ## The OESSOTokenManager bean is the primary component for the verification - ## and generation of OECP SSO tokens. The OESSOTokenManager works a - ## supporting role for other Spring filter beans that are actively involved - ## in the HTTP request authentication process. - ## - OESSOTokenManager.tokenPolicy=disabled - OESSOTokenManager.ssoTokenURLOption=OECP - OESSOTokenManager.ssoTokenExpires=3600 - OESSOTokenManager.ssoAllowScope= - OESSOTokenManager.ssoGrantScope= - OESSOTokenManager.ssoTokenRefresh=true - OESSOTokenManager.ssoRefreshDeltaTime=3600 - OESSOTokenManager.springRolePrefix= - - ## Properties for the OESSOFilter bean (see authFilters.xml) - ## The OESSOFilter bean is injected into the HTTP authentication process - ## to look for HTTP [Authorization] header that contains an OECP SSO token. - ## If no header and token is found, it passes control to the next step - ## in the authentication process. If a header and token is found it will - ## use the OESSOTokenManager to extract, verify, and convert the OECP - ## SSO token into a native OE ClientPrincipal token that is passed to the - ## ABL session. - ## - ## The OESSOFilter bean has properties that control what header to look - ## for in the HTTP request, what the header's authentication-scheme name - ## is, and whether OECP SSO is enabled. - OESSOFilter.authPolicy=disabled - OESSOFilter.authClientType=* - - ## Properties for the OESSORefreshFilter bean (see authFilters.xml) - ## The OESSORefreshFilter bean is injected into the authentication process - ## and intercepts client requests to refresh an expired OECS SSO token. - ## If no request is found, the bean passes the request to the next step - ## in the authentication process. When a request if found, it interfaces - ## with the OESSOTokenManager to validate the refresh request and issue - ## an updated OECP SSO token that has a new expiration date. - ## - ## The OESSORefreshFilter has properties that allow it to recognize when - ## a HTTP request is asking to refresh a SSO token. - OESSORefreshFilter.refreshURL=/static/auth/token - OESSORefreshFilter.refreshClientType=* - - - ########## 'ldap' Authentication Manager properties ####################### - ## Required LDAP Server Authentication Manager configuration properties - - ## Required LDAP URL: {ldap|ldaps}://: - ldap.url=ldap://hostname:389 - - ## Required Directory Server ROOT DN. Unique, per installation, value - ldap.root.dn=dc=acme,dc=com - - ## Required LDAP login user account UPN/DN used to locate the LDAP user - ## account being authenticated and obtain its login ID (i.e. DN) - ## - ## Generic LDAP Server's MUST be an LDAP DN of the user account - ## Example: cn=fred,ou=users,dc=acme,dc=com - ## - ## Active Directory LDAP server MAY be an LDAP DN, OR it may be an - ## UPN (User Principal Name) Example: username@ad-domain - ## - ldap.manager-dn=bsmithf@acme.com - - ## The ldap.manager-password is used with the ldap.manager-dn user account. - ## It has no affect on the user being authenticated. The value may be - ## clear-text or a value generated by the utility: - ## DLC/bin/stspwdutil encrypt - ## - ldap.manager-password=secret - - ## Where and how to being searching for the user's account being authenticated - ldap.usersearch.base= - ldap.usersearch.searchSubtree=true - - ## Default LDAP Server user account search filter. Edit if necessary. - ## ActiveDirectory [default]: (|(userPrincipalName={0}) (sAMAccountName={0}) (mail={0}) (cn={0})) - ## Generic LDAP Directory: (|(mail={0}) (cn={0})) - ldap.usersearch.filter=(|(userPrincipalName={0}) (sAMAccountName={0}) (mail={0}) (cn={0})) - - - ## Where and how to search for the authenticated user's LDAP groups, and how to - ## convert the located LDAP group object's attribute value into a Spring ROLE - ldap.groupsearch.base= - ldap.grouprole.attribute=cn - ldap.groupsearch.searchSubtree=true - - ## Search for the existence of an LDAP group the authenticated LDAP user - ## account has been granted membership in. Each LDAP group object found - ## is used as the Spring/Client-Principal ROLE attributes - ## Active Directory [default]: (&(objectclass=group) (member={0})) - ## Generic LDAP Directory: (|(&(objectclass=groupofnames) (member={0})) (&(objectclass=groupofuniquenames) (uniqueMember={0})) ) - ldap.groupsearch.filter=(&(objectclass=group) (member={0})) - - ##--------------------------------------------------------------------------- - ## Optional LDAP Server Authentication Manager configuration properties - ##--------------------------------------------------------------------------- - - - ## Follow LDAP server referral objects - ldap.contxtSrc.referral=ignore - - ## Connection/read timeout in seconds - ldap.contxtSrc.timeout=5000 - - ## Prefix for LDAP group attribute name to identify it as a Spring ROLE name - ldap.authpopulator.rolePrefix=ROLE_ - - ## Ignore Active Directory exceptions for very large return result-sets - ldap.authpopulator.ignorePartialResultException=true - - ## Convert all LDAP group attribute names used as Spring ROLES to uppercase - ldap.authpopulator.convertToUpperCase=true - - ########## 'oerealm' Authentication Manager properties ####################### - ## Properties that connects to an MS-Agent Realm service and uses it as a - ## source of user account information during the Spring authentication process. - ## These properties are special case versions of the ClientPrincipalFilter - ## property set and only apply to the 'oerealm' Authentication Manager. - ## - OERealm.AuthProvider.multiTenant=true - OERealm.AuthProvider.userDomain= - OERealm.AuthProvider.expires=0 - - ## Encrypted file containing OE Domain Access-codes used to seal - ## Client-Principal tokens - OERealm.AuthProvider.registryFile=ABLDomainRegistry.keystore - - ## Properties for the 'oerealm' Authentication Manager's use of the - ## MS-Agent's OERealm server OOABL class - ## - OERealm.UserDetails.realmClass=OpenEdge.Security.Realm.HybridRealm - OERealm.UserDetails.grantedAuthorities=ROLE_PSCUser - OERealm.UserDetails.appendRealmError=false - OERealm.UserDetails.propertiesAttrName= - OERealm.UserDetails.userIdAttrName= - - ## The file holding a ClientPrincipal token used to authenticate - ## the PASOE server to the OERealm ABL class. - OERealm.UserDetails.realmTokenFile= - - - - ########## 'ad' Authentication Manager properties ######################### - ## Required properties for the 'ad' (Active Directory) Authentication - ## Manager. - ## For all Active Directory authentication manager configuration - ## property details refer to: - ## conf/oeablSecurity.properties.README - ## - ad.ldap.url=ldap://hostname:389 - ad.ldap.rootdn=dc=acme,dc=com - - ## Optional properties for the 'ad' (Active Directory) Authentication - ## Manager. - ## - ad.user.domain=acme.com - ad.AuthoritiesMapper.prefix=ROLE_ - ad.AuthoritiesMapper.convertToUpperCase=true - - ########## 'sts' Authentication Manager properties ############################ - ## OpenEdge Authentication Gateway client configuration - ## for direct user logins to a PASOE server - ## - - ## Required Authentication Gateway URL - sts.UserDetails.stsURL=https://host:port - sts.UserDetails.stsKeystore= - - ## How to handle user-id Domain fields - sts.AuthProvider.multiTenant=true - sts.AuthProvider.registryFile=ABLDomainRegistry.keystore - sts.AuthProvider.userDomain= - - ## TLS connection to Authentication Gateway Server - sts.UserDetails.noHostVerify=false - sts.UserDetails.tlsCipherSuites= - sts.UserDetails.tlsProtocols= - sts.UserDetails.sniHost= - - sts.JwtTokenExchange.stsURL= - sts.JwtTokenExchange.stsKeystore= - sts.JwtTokenExchange.clientHeaderName=x-oests-token - sts.JwtTokenExchange.userAgent=PASOE (Spring) - sts.JwtTokenExchange.certLocation=${psc.as.oe.dlc}/certs - sts.JwtTokenExchange.noHostVerify=false - sts.JwtTokenExchange.tlsCipherSuites= - sts.JwtTokenExchange.tlsProtocols= - sts.JwtTokenExchange.sniHost= - -sts.Saml2TokenExchange.stsURL= -sts.Saml2TokenExchange.stsKeystore= -sts.Saml2TokenExchange.clientHeaderName=x-oests-token -sts.Saml2TokenExchange.userAgent=PASOE (Spring) -sts.Saml2TokenExchange.certLocation=${psc.as.oe.dlc}/certs -sts.Saml2TokenExchange.noHostVerify=true -sts.Saml2TokenExchange.tlsCipherSuites= -sts.Saml2TokenExchange.tlsProtocols= -sts.Saml2TokenExchange.sniHost= - ########## 'oauth2' Login Model properties ############################### - ## Properties for the 'oauth2' Login Model that supplies OAuth2 authorization - ## handling for 'Resource Servers' web data service access and - ## 'Authorization Servers' for obtaining access & refresh tokens to access a - ## Resource server's data. - ## - ## Configuring the 'oauth2' Login Model involves 3 levels: - ## 1) JWT Access/ID token validation (jwtToken.* properties) - ## 2) OAuth2 Resource Server run-time operations and coordination with OAuth2 - ## Authorization Server who issues the JWT tokens - ## (oauth2.resSvc.* properties) - ## 3) Client-Principal generation (see ClientPrincipalFilter.* properties) - ## - - ## Required enable/disable of the OAuth2 Resource server support. - ## The allowable values are {enable} - oauth2.ResourceServer.enable=enable - - ## Required JWT token handler properties for validating the inbound - ## JWT/OAuth2 ID token passed by the OAuth2 client as a Bearer token. - - ## The JWT Signature algorithm used by the Authorization Server - jwtToken.signatureAlg=RS256 - - ## The method of obtaining Public/Secret encryption keys from the - ## Authorization Server. Each type has separate properties - ## following: - jwtToken.keystore.type=jwk - - ## "jwk": URL path to Authorization Server's JWK distribution - jwtToken.keystore.jwkurl=https://localhost:8881/path-to-jwk-set - - ## "jwkissuer": URL path to Authorization Server's provider configuration url - jwtToken.keystore.jwkIssuerUrl=https://idp.example.com - - ## "mac": JWT Signature's mac secret-key phrase - jwtToken.macKey=oeph3::B8E894037D0A296A0908F2FAFB0A0148 - - ## "pkcs12": JWT Signature's public-key storage and access - jwtToken.keystore.path=${catalina.base}/ablapps/${oeabl.ablapp.name}/conf/${oeabl.ablapp.name}.p12 - jwtToken.keystore.userid= - jwtToken.keystore.pwd=oeph0::76E5F6C162276768465F02E4D2D1DDCD - jwtToken.keystore.alias=sample - - ## "jwe": JWT Encryption - jwtToken.keystore.jwe.key.selector=noJWEKeySelector - jwtToken.keystore.jwe.path= - jwtToken.keystore.jwe.pwd= - jwtToken.keystore.jwe.alias= - jwtToken.keystore.jwe.cache=true - - ## After Signature validation, extract key JWT token key assertion values - jwtToken.defaultRoles= - jwtToken.usernameField=sub - jwtToken.mapScopeToRole=true - jwtToken.scopeToRolePrefix=scope. - jwtToken.includeAllClaims=true - jwtToken.scopeNameField=scope - - # Clock skew for validating jwt timestamp - jwtToken.validation.clockSkew=60 - - ## OAuth2 Resource server configuration - ## An OAuth2 Resource server provides data-services for client applications. The client application - ## sends an "access token" obtained from an OAuth2 Authorization server, which the Resource server - ## must validate before it is accepted. Validation involves obtaining a JWT token that can be - ## validated by the Resource server and its "claims" used to perform URL access control to the - ## Resource service's data-services. The JWT may be obtained as a "self-contained" access token - ## or by using the access token as an indirect reference to a JWT stored in the issuing Authorization - ## server. - ## - oauth2.resSvc.tokenServices=oauth2 - oauth2.resSvc.audience=oeablapp - oauth2.resSvc.strictAudience=false - oauth2.resSvc.realmName=oeoauth - oauth2.resSvc.userDetailsPrefix=noOP - oauth2.resSvc.clientId=oeablClient - - ## Configuration for remote validation of tokens (blank uri disables remote validation) - # Client secret may be encrypted using genpassword, e.g. oeph0::76E5F6C162276768465F02E4D2D1DDCD - oauth2.opaqueToken.introspectionUri= - oauth2.opaqueToken.clientSecret= - - ## Configuration for publishing public keys - publicKeys.keystore.path=${catalina.base}/ablapps/${oeabl.ablapp.name}/conf/${oeabl.ablapp.name}.p12 - publicKeys.keystore.pwd=changeme - publicKeys.keystore.aliases=changeme - publicKeys.filter.url=/oauth2/keys - - ########## 'saml' Authention Manager properties ############################ - - ## Location of Service Provider metadata xml file (blank dynamically generates metadata) - # samlToken.metadata.spMetaDataFileLocation=classpath:../metadata/sp.xml - # samlToken.metadata.spMetaDataFileLocation=file:///c:/OpenEdge/WRK/oepas1/webapps/ROOT/WEB-INF/metadata/sp.xml - samlToken.metadata.spMetaDataFileLocation= - - ## Location of Identity Provider metadata xml file - # samlToken.metadata.idpMetaDataFileLocation=https://example.okta.com/app/myappid/sso/saml/metadata - # samlToken.metadata.idpMetaDataFileLocation=file:///c:/OpenEdge/WRK/oepas1/webapps/ROOT/WEB-INF/metadata/idp.xml - samlToken.metadata.idpMetaDataFileLocation=classpath:../metadata/idp.xml - - # URL for retrieving Service Provider metadata - samlToken.metadata.url=/saml2/metadata/${samlToken.serviceProvider.registrationId} - - # comma separated list of audiences - samlToken.validation.validAudiences=${samlToken.serviceProvider.entityId} - - # comma separated list of recipients - # samlToken.validation.validRecipients=${samlToken.serviceProvider.baseUrl}/saml2/login/{samlToken.serviceProvider.registrationId} - samlToken.validation.validRecipients= - - # clock skew expressed as seconds - samlToken.validation.clockSkew=60 - - # validate InResponseTo assertion (default to true) - samlToken.validation.validateInResponseToAssertion=true - - # list of valid saml destination assertions - # if this property is blank, then it defaults to the samlToken.serviceProvider.assertionConsumerServiceLocation - # if both assertionConsumerServiceLocation and validDestinations is blank - # then destination validation is disabled - samlToken.validation.validDestinations= - - # Service provider registration ID - samlToken.serviceProvider.registrationId=${oeabl.ablapp.name} - - # Service provider entity ID - samlToken.serviceProvider.entityId=${samlToken.serviceProvider.baseUrl}${samlToken.metadata.url} - - # Service provider authentication filter URL - samlToken.serviceProvider.authenticationUrl=/saml2/login/${samlToken.serviceProvider.registrationId} - - # Service provider assertion URL - samlToken.serviceProvider.assertionConsumerServiceLocation=${samlToken.serviceProvider.baseUrl}${samlToken.serviceProvider.authenticationUrl} - - # Service provider authentication URL - samlToken.serviceProvider.authenticationRequestUrl=/saml2/authenticate/${samlToken.serviceProvider.registrationId} - - # Service provider redirect parameter name - samlToken.serviceProvider.redirectProperty=RelayState - - # Always use the default target URL - samlToken.serviceProvider.alwaysUseDefaultTargetUrl=false - - # The default target URL - samlToken.serviceProvider.defaultTargetUrl=/ - - ## Allow Binding Methods of incoming request with - ## SAML token - samlToken.httpBinding.allowMethods=GET,POST,PUT,DELETE - - ## SAML UserDetails - ## Usually roles comes as part of assertion attribute of SAML token - ## If there roles are configured with multiple attributes then use comma separated list of attributes - samlToken.UserDetails.roleAttrName=Attribute1,Attribute2 - - ## If there is not roles found in SAMl token then use default roles as PSCUser - samlToken.UserDetails.defaultGrantedAuthorities=PSCUser - samlToken.UserDetails.rolePrefix=ROLE_ - - ## If the SAML token's user-id does not contain a 'domain', use this default - samlToken.UserDetails.userDomain= - - ## Defines additional user authorities using User Details service. - ## Bean name structure: ${samlToken.UserDetails.beanPrefix}UserDetailsService - ## Currently implemented: - ## - noOPUserDetailsService - no UserDetail service - ## - inMemoryUserDetailsService - using user.properties file - samlToken.UserDetails.beanPrefix=noOP - - ## Required location of the OE Domain encrypted Access-code file used to seal - ## Client-Principal tokens with - samlToken.UserDetails.registryFile= - - ## SAML logout properties - #SAML logout URI - samlLogout.request.uri=/saml2/logout - #Successful logout redirect URL (optional) - samlLogout.success.url= - #Successful logout message if logout redirect URL is not set - samlLogout.success.message=Logout executed - #URI SAML IdP provided uses to send logout result token - samlLogout.idp.result.uri=/saml2/logout/result - #Path to keystore to store SAML logout token signature key - samlLogout.keystore.path=${catalina.base}/ablapps/${oeabl.ablapp.name}/conf/${oeabl.ablapp.name}.p12 - #Keystore type defined by samlLogout.keystore.path - samlLogout.keystore.type=PKCS12 - #Keystore path defined by samlLogout.keystore.path - samlLogout.keystore.password=oeph0::76E5F6C162276768465F02E4D2D1DDCD - #Key alias to sign SAML logout token - samlLogout.request.signature.key=defsigkey - - - ########## MDC (Mapped Diagnostic) properties ############################ - ## MDC HTTP request header enablement. Only - ## the HTTP headers included in this list will - ## be available as MDC token fields in ABL SessionManager log files. - MDC.filter.headerList= diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README b/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README deleted file mode 100644 index 0a5e369..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright © 1998-2019 Progress Software Corporation and/or one of its subsidiaries or affiliates. All rights reserved. - ----------------------- oeablSecurity.properties.README ------------------------- -The oeablSecurity.properties file found in an oeabl web application's WEB-INF/ -directory holds configuration property values that are specific to the web -application. Properties not defined in this web application's -oeablSecurity.properties file are inherited from the full version found in -the PAS for OE instance's conf/ directory. - -The PAS for OE instance's conf/oeablSecurity.properties file holds a full set -of properties and their default values. In the same conf/ directory is the -oeablSecurity.properties.README that describes the full set of properties -supported by the instance. - -You are free to copy property definitions from the conf/ directory's -oeablSecurity.properties file into this web application's version to explicitly -define the property. You may also remove a property from the web application's -oeablSecurity.properties file to begin using the default value found in the -conf/ directory's version. - -To avoid this file's becoming out of date with the PAS for OE instance, -please refer to the the oeablSecurity.properties.README file found in the -conf/ directory. - ---------------------------------------------------------------------------- diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.xml b/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.xml deleted file mode 100644 index bae32bb..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - oeablSecurity.xml - - - - - - - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv b/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv deleted file mode 100644 index f28d459..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv +++ /dev/null @@ -1,49 +0,0 @@ - -############## Intercept-url definitions for the REST transport URIs ############### -"/rest/**","*","hasAnyAuthority('scope.PSCUser')" - -############## Intercept-url definitions for the WEB transport URIs ############### -"/web/**","*","hasAnyAuthority('scope.PSCUser')" - -############## Intercept-url definitions for the default URI space ################# -"/static/home.html","GET","hasAuthority('scope.PSCUser')" - -"/static/ServerStatus.html","GET","hasAnyAuthority('scope.PSCUser','scope.PSCAdmin','scope.PSCDebug')" - -"/server/**","GET","hasAnyAuthority('scope.PSCAdmin','scope.PSCDebug')" - -"/oauth2/**","GET","permitAll()" - -"/*","GET","permitAll()" - -"/static/*","GET","permitAll()" - -"/static/error/*","GET","permitAll()" - -"/static/images/*","GET","permitAll()" - -"/static/auth/*","GET","permitAll()" - -"/static/**","GET","hasAnyAuthority('scope.PSCUser')" - -"/**/*.htm*","GET","hasAnyAuthority('scope.PSCUser')" - -"/**/*.gif","GET","hasAnyAuthority('scope.PSCUser')" - -"/**/*.jpg","GET","hasAnyAuthority('scope.PSCUser')" - -"/**/*.css","GET","hasAnyAuthority('scope.PSCUser')" - -"/**/*.js","GET","hasAnyAuthority('scope.PSCUser')" - -"/**/*.json","GET","hasAnyAuthority('scope.PSCUser')" - -"/**/*.asp","GET","hasAnyAuthority('scope.PSCUser')" - -"/**/*.inc","GET","hasAnyAuthority('scope.PSCUser')" - -# Best practice - deny anything not explicitly granted -"/**","*","denyAll()" - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt b/SportsApp/Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt deleted file mode 100644 index 067c27e..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt +++ /dev/null @@ -1 +0,0 @@ -This directory can be used to store r-code related specifically to the web application you are deploying. \ No newline at end of file diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/security.tld b/SportsApp/Sports/PASOEContent/WEB-INF/security.tld deleted file mode 100644 index 9c3b4b7..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/security.tld +++ /dev/null @@ -1,195 +0,0 @@ - - - - Spring Security Authorization Tag Library - - 5.2 - security - http://www.springframework.org/security/tags - - - - A tag which outputs the body of the tag if the configured access expression - evaluates to true for the currently authenticated principal. - - authorize - org.springframework.security.taglibs.authz.JspAuthorizeTag - JSP - - - - A Spring-EL expression which is supported by the WebSecurityExpressionHandler - in the application context. The latter will be used to evaluate the expression. - - access - false - true - - - - - A URL within the application. If the user has access to this URL (as determined by - the AccessDecisionManager), the tag body will be evaluated. If not, it will - be skipped. - - url - false - true - - - - - Can be used to specify the HTTP method (typically GET or POST) which is used in combination - with the URL when consulting the AccessDecisionManager. Only has any meaning when used in combination - with the "url" attribute. Defaults to GET. - - method - false - false - - - - - A page scoped variable into which the boolean result of the tag evaluation will be written, allowing the - same condition to be reused subsequently in the page without re-evaluation. - - var - false - false - - - - - - Allows access to the current Authentication object. - - authentication - org.springframework.security.taglibs.authz.AuthenticationTag - empty - - - - Property of the Authentication object which should be output. Supports nested - properties. For example if the principal object is an instance of UserDetails, - the property "principal.username" will return the username. Alternatively, using - "name" will call getName method on the Authentication object directly. - - property - true - true - - - - Name of the exported scoped variable which will contain the - evaluated property of the Authentication object. - - var - false - false - - - - Set HTML escaping for this tag, as a boolean value. - - htmlEscape - false - true - - - - Scope for var. - - scope - false - false - - - - - - Allows inclusion of a tag body if the current Authentication - has one of the specified permissions to the presented - domain object instance. - - accesscontrollist - org.springframework.security.taglibs.authz.AccessControlListTag - JSP - - - - A comma separated list of permissions, which will be converted to - Permission instances by the configured PermissionFactory. - - hasPermission - true - true - - - - The actual domain object instance for which permissions - are being evaluated. - - domainObject - true - true - - - - A page scoped variable into which the boolean result of the tag evaluation will be written, allowing the - same condition to be reused subsequently in the page without re-evaluation. - - var - false - false - - - - - tags, but if for some reason you cannot use - this tag is a handy replacement. You should place this tag within an HTML

block, - where you would normally place other s. Do NOT place this tag within a Spring - block—Spring Security handles Spring forms automatically. - ]]> - csrfInput - org.springframework.security.taglibs.csrf.CsrfInputTag - empty - - - - block, where - you would normally place other meta tags. Once you use this tag, you can access the form field name using - the JQuery $("meta[name='_csrf_parameter']").attr("content") and the header name using - $("meta[name='_csrf_header']").attr("content"). Likewise, you can access the token value with - $("meta[name='_csrf']").attr("content"). You should use a form field when creating and submitting forms from - JavaScript, and you should use a header when sending AJAX requests. If CSRF protection is not enabled, this - tag outputs nothing. - ]]> - csrfMetaTags - org.springframework.security.taglibs.csrf.CsrfMetaTagsTag - empty - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/spring/properties-loader.xml b/SportsApp/Sports/PASOEContent/WEB-INF/spring/properties-loader.xml deleted file mode 100644 index f56c856..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/spring/properties-loader.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - file:${catalina.home}/conf/oeablSecurity.properties - file:${catalina.base}/conf/oeablSecurity.properties - file:${catalina.base}/ablapps/${oeabl.ablapp.name}/conf/oeablSecurity.properties - file:${catalina.base}/webapps/${oeabl.webapp.name}/WEB-INF/oeablSecurity.properties - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/tlr/Merge.template b/SportsApp/Sports/PASOEContent/WEB-INF/tlr/Merge.template deleted file mode 100644 index 1d32f24..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/tlr/Merge.template +++ /dev/null @@ -1,30 +0,0 @@ -# See $CATALINA_HOME/conf/openedge.properties.README for details on the properties below. -# DO NOT MODIFY any ${} tags - -# Transport properties for the APSV protocol -[${oepas-app}.${oepas-webapp}.APSV] - adapterEnabled=1 - enableRequestChunking=1 - useHTTPSessions=1 - -# Transport properties for the SOAP protocol -[${oepas-app}.${oepas-webapp}.SOAP] - adapterEnabled=1 - adminEnabled=1 - adminSoapAction=urn:services-progress-com:wsa-admin:01 - debugClients= - wsaUrl=http://${psc.as.host.name}:${psc.as.http.port}/${oepas-webapp}/soap - wsdlEnabled=1 - -# Transport properties for the REST protocol -[${oepas-app}.${oepas-webapp}.REST] - adapterEnabled=1 - -# Transport properties for the WEB protocol -[${oepas-app}.${oepas-webapp}.WEB] - adapterEnabled=1 - defaultCookieDomain= - defaultCookiePath= - defaultHandler=OpenEdge.Web.CompatibilityHandler - srvrDebug=0 - wsRoot=/${oepas-webapp}/static/webspeed diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr b/SportsApp/Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr deleted file mode 100644 index e69de29..0000000 diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/users.properties b/SportsApp/Sports/PASOEContent/WEB-INF/users.properties deleted file mode 100644 index 7292d17..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/users.properties +++ /dev/null @@ -1,4 +0,0 @@ -restuser=password,ROLE_PSCUser,enabled -restdebug=password,ROLE_PSCUser,ROLE_PSCAdmin,ROLE_PSCDebug,enabled -restadmin=password,ROLE_PSCUser,ROLE_PSCAdmin,enabled -restnone=password,ROLE_None,enabled diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/web.xml b/SportsApp/Sports/PASOEContent/WEB-INF/web.xml deleted file mode 100644 index 716dc2e..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/web.xml +++ /dev/null @@ -1,585 +0,0 @@ - - - Progress Application Server for OpenEdge - - - - - - - - logback/configuration-resource - java.lang.String - ../logging.xml - - - - - detailLevel - 0 - - - - - - contextConfigLocation - - - - /WEB-INF/oeablSecurity.xml - /WEB-INF/adapters/soap/camel-spring.xml - - - - - - - - contextInitializerClasses - com.progress.appserv.services.security.OESpringPropertySource - - - - - ch.qos.logback.classic.selector.servlet.ContextDetachingSCL - - - - - contextClass - - com.progress.appserv.services.security.OEXmlWebApplicationContext - - - - - - com.progress.appserv.services.security.OESpringContextLoaderListener - - - - - - org.springframework.security.web.session.HttpSessionEventPublisher - - - - - - com.progress.appserv.oeabl.OEAblServletContextListener - - - - - com.progress.appserv.adapters.apsv.OEAiaServletContextListener - - - - com.progress.appserv.adapters.camel.soap.OESoapServletContextListener - - - - com.progress.appserv.adapters.rest.OERestServletContextListener - - - - com.progress.appserv.adapters.web.OEWebServletContextListener - - - - com.progress.appserv.clientrt.broker.SessionLifeCycleListener - - - - - springSecurityFilterChain - org.springframework.web.filter.DelegatingFilterProxy - - - - springSecurityFilterChain - /* - - - - - mvc-dispatch - org.springframework.web.servlet.DispatcherServlet - - contextClass - com.progress.appserv.services.security.OEXmlWebApplicationContext - - - contextConfigLocation - pasoe:mvc-dispatch-context.xml - - 1 - - - - - - - - archiveLocation - WEB-INF/adapters/rest/ - - - - - - - - - - runTimeProperties - WEB-INF/adapters/rest/runtime.props - - - - - - - - - - - - - LoggerContextFilter - ch.qos.logback.classic.selector.servlet.LoggerContextFilter - - - LoggerContextFilter - /* - - - - - OERestAdapterFilter - OERestAdapterFilter - com.progress.appserv.adapters.rest.OERestAdapterFilter - - - OERestAdapterFilter - OERestAdapter - - - - OEAblServletFilter - OEAblServletFilter - com.progress.appserv.oeabl.OEAblServletFilter - - - OEAblServletFilter - /* - - - - - - - - - - - - - - - - - - - - - - ablservice - com.progress.appserv.oeabl.ABLService - 1 - - - - ablservice - /server/* - - - - - APSV Transport Filter - com.progress.appserv.adapters.apsv.PingFilter - - - APSV Transport Filter - apsv - - - - apsv - com.progress.appserv.adapters.apsv.Aia - - instanceName - Aia1 - - 2 - - - - apsv - /apsv/* - - - - - - REST Transport Filter - com.progress.appserv.adapters.rest.PingFilter - - - REST Transport Filter - OERestAdapter - - - - OERestAdapter - com.progress.appserv.adapters.rest.OERestCXFNonSpringServlet - - redirects-list - - .*\.jsp - .*\.jspx - - - - redirect-servlet-name - mvc-dispatch - - - static-resources-list - - .*\.html - .*\.htm - .*\.json - .*\.js - - - 3 - - - - - OERestAdapter - /rest/* - - - - - - WEB Transport Filter - com.progress.appserv.adapters.web.PingFilter - - - WEB Transport Filter - OEWebServlet - - - - OEWebServlet - com.progress.appserv.adapters.web.OEWeb - - redirects-list - - .*\.jsp - .*\.jspx - - - - redirect-servlet-name - mvc-dispatch - - - static-resources-list - - .*\.html - .*\.htm - .*\.json - .*\.js - - - - OEWebHandlerClass - Progress.Web.InternalWebHandler - - 4 - - - - - OEWebURL - /web - - - OEWebServlet - /web/* - - - - - - - - - - - - - soapAdapterLocation - /WEB-INF/adapters/soap/ - - - - - CamelSOAPServlet - - com.progress.appserv.adapters.camel.soap.CamelSOAPServlet - - - ignoreDuplicateServletName - true - - 6 - - - CamelSOAPServlet - /soap/* - - - - - index.html - index.jsp - static/index.html - static/index.jsp - - - - - - - - /WEB-INF/jsp/errorPage.jsp - - - - - java.lang.Throwable - /WEB-INF/jsp/exceptionPage.jsp - - - - - diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/web.xml-clientcert b/SportsApp/Sports/PASOEContent/WEB-INF/web.xml-clientcert deleted file mode 100644 index 0d7f1b0..0000000 --- a/SportsApp/Sports/PASOEContent/WEB-INF/web.xml-clientcert +++ /dev/null @@ -1,583 +0,0 @@ - - - Progress Application Server for OpenEdge - - - - - - - - logback/configuration-resource - java.lang.String - ../logging.xml - - - - - detailLevel - 0 - - - - - - contextConfigLocation - - - - /WEB-INF/oeablSecurity.xml - /WEB-INF/adapters/soap/camel-spring.xml - - - - - - - - contextInitializerClasses - com.progress.appserv.services.security.OESpringPropertySource - - - - - ch.qos.logback.classic.selector.servlet.ContextDetachingSCL - - - - - contextClass - - com.progress.appserv.services.security.OEXmlWebApplicationContext - - - - - - org.springframework.web.context.ContextLoaderListener - - - - - - org.springframework.security.web.session.HttpSessionEventPublisher - - - - - - com.progress.appserv.oeabl.OEAblServletContextListener - - - - - com.progress.appserv.adapters.apsv.OEAiaServletContextListener - - - - com.progress.appserv.adapters.camel.soap.OESoapServletContextListener - - - - com.progress.appserv.adapters.rest.OERestServletContextListener - - - - com.progress.appserv.adapters.web.OEWebServletContextListener - - - - com.progress.appserv.clientrt.broker.SessionLifeCycleListener - - - - - springSecurityFilterChain - org.springframework.web.filter.DelegatingFilterProxy - - - - springSecurityFilterChain - /* - - - - - mvc-dispatch - org.springframework.web.servlet.DispatcherServlet - - contextClass - com.progress.appserv.services.security.OEXmlWebApplicationContext - - - contextConfigLocation - pasoe:mvc-dispatch-context.xml - - 1 - - - - - - - - archiveLocation - WEB-INF/adapters/rest/ - - - - - - - - - - runTimeProperties - WEB-INF/adapters/rest/runtime.props - - - - - - - - - - - - - LoggerContextFilter - ch.qos.logback.classic.selector.servlet.LoggerContextFilter - - - LoggerContextFilter - /* - - - - - OERestAdapterFilter - OERestAdapterFilter - com.progress.appserv.adapters.rest.OERestAdapterFilter - - - OERestAdapterFilter - OERestAdapter - - - - OEAblServletFilter - OEAblServletFilter - com.progress.appserv.oeabl.OEAblServletFilter - - - OEAblServletFilter - /* - - - - - - - - - - - - - - - - - PASOE oeabl CORS pre-flight constraint - - CORS preflight access - /* - OPTIONS - - - - - PASOE oeabl Security Constraint - - Protected Area - /* - - - ROLE_PSCAdmin - ROLE_PSCOper - ROLE_PSCUser - ROLE_PSCNone - - - - - CLIENT-CERT - OpenEdge - - - - ROLE_PSCAdmin - - - ROLE_PSCOper - - - ROLE_PSCUser - - - ROLE_PSCNone - - - - - - - - ablservice - com.progress.appserv.oeabl.ABLService - 1 - - - - ablservice - /server/* - - - - - APSV Transport Filter - com.progress.appserv.adapters.apsv.PingFilter - - - APSV Transport Filter - apsv - - - - apsv - com.progress.appserv.adapters.apsv.Aia - - instanceName - Aia1 - - 2 - - - - apsv - /apsv/* - - - - - - REST Transport Filter - com.progress.appserv.adapters.rest.PingFilter - - - REST Transport Filter - OERestAdapter - - - - OERestAdapter - com.progress.appserv.adapters.rest.OERestCXFNonSpringServlet - - redirects-list - - .*\.jsp - .*\.jspx - - - - redirect-servlet-name - mvc-dispatch - - - static-resources-list - - .*\.html - .*\.htm - .*\.json - .*\.js - - - 3 - - - - - OERestAdapter - /rest/* - - - - - - WEB Transport Filter - com.progress.appserv.adapters.web.PingFilter - - - WEB Transport Filter - OEWebServlet - - - - OEWebServlet - com.progress.appserv.adapters.web.OEWeb - - redirects-list - - .*\.jsp - .*\.jspx - - - - redirect-servlet-name - mvc-dispatch - - - static-resources-list - - .*\.html - .*\.htm - .*\.json - .*\.js - - - - OEWebHandlerClass - Progress.Web.InternalWebHandler - - 4 - - - - - OEWebURL - /web - - - OEWebServlet - /web/* - - - - - - - - - - - - - soapAdapterLocation - /WEB-INF/adapters/soap/ - - - - - CamelSOAPServlet - - com.progress.appserv.adapters.camel.soap.CamelSOAPServlet - - - ignoreDuplicateServletName - true - - 6 - - - CamelSOAPServlet - /soap/* - - - - - index.html - index.jsp - static/index.html - static/index.jsp - - - - - - - - /WEB-INF/jsp/errorPage.jsp - - - - - java.lang.Throwable - /WEB-INF/jsp/exceptionPage.jsp - - - - - diff --git a/SportsApp/Sports/PASOEContent/favicon.ico b/SportsApp/Sports/PASOEContent/favicon.ico deleted file mode 100644 index 4b72ec72e65b23565a83a5a0e37c931ded0760f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 370070 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�E{o*&+f3o)f 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 J*`-$N{{sm_J(~ak diff --git a/SportsApp/Sports/PASOEContent/index.jsp b/SportsApp/Sports/PASOEContent/index.jsp deleted file mode 100644 index e9c7b29..0000000 --- a/SportsApp/Sports/PASOEContent/index.jsp +++ /dev/null @@ -1,32 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - errorPage="/WEB-INF/jsp/errorPage.jsp"%> -<% // Forward to the home page based on being an - // development versus a production server instance - String product = "dev"; - - product = System.getProperty("psc.as.security.model"); - if (product == null ) { - product = "default"; - } - - if ( product.matches("^dev.*") ) { - // For development server instances, redirect the client to the - // static/ServerStatus.html static page (aka static/index.html) - // that the web application tailoring sets up as the home page. - // The redirect is a result of the static page needing the - // correct location for relative URLs to work. - response.sendRedirect(request.getScheme() + "://" + - request.getServerName() + ":" + - request.getServerPort() + - request.getContextPath() + - "/static/index.html"); - } else { - // For production server instance or an untailored default, forward - // directly to the home JSP page because it can dynamically insert - // the necessary URL path elements. - request.getRequestDispatcher("/static/home.jsp").forward(request, response); - } -%> diff --git a/SportsApp/Sports/PASOEContent/static/ServerStatus.html b/SportsApp/Sports/PASOEContent/static/ServerStatus.html deleted file mode 100644 index d7ea3e0..0000000 --- a/SportsApp/Sports/PASOEContent/static/ServerStatus.html +++ /dev/null @@ -1,132 +0,0 @@ - - - - - -Progress Application Server for OpenEdge - - - - - - - - - - -
-
-
-
- Apache Icon -
-
-

-

-
-
-
-
-
- -
-
Tomcat Version
-
-
- -
-
PAS Version
-
-
- -
-
OS Name
-
-
- -
-
OS Version
-
-
- -
-
OS Architecture
-
-
- -
-
- -
-
JVM Vendor
-
-
- -
-
JVM Version
-
-
- -
-
Hostname
-
-
- -
-
IP Address
-
-
- -
-
- -
- -        - -
- -
- -
- -
-
- - - - - - diff --git a/SportsApp/Sports/PASOEContent/static/SportsService.json b/SportsApp/Sports/PASOEContent/static/SportsService.json deleted file mode 100644 index 592f098..0000000 --- a/SportsApp/Sports/PASOEContent/static/SportsService.json +++ /dev/null @@ -1,935 +0,0 @@ -{ - "version": "1.4", - "lastModified": "Wed Feb 06 14:27:17 IST 2019", - "services": [{ - "name": "SportsService", - "address": "\/rest\/SportsService", - "useRequest": true, - "resources": [ - { - "name": "Customer", - "path": "\/Customer", - "autoSave": false, - "schema": { - "type": "object", - "additionalProperties": false, - "properties": {"dsCustomer": { - "type": "object", - "additionalProperties": false, - "properties": {"ttCustomer": { - "type": "array", - "idProperty": "id", - "items": { - "additionalProperties": false, - "properties": { - "_id": { - "type": "string", - "semanticType": "Internal" - }, - "_errorString": { - "type": "string", - "semanticType": "Internal" - }, - "id": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "id" - }, - "seq": { - "type": "integer", - "ablType": "INTEGER", - "default": null, - "title": "seq" - }, - "CustNum": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Cust Num" - }, - "Name": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Name" - }, - "Address": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Address" - }, - "Address2": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Address2" - }, - "Balance": { - "type": "number", - "ablType": "DECIMAL", - "default": 0, - "title": "Balance" - }, - "City": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "City" - }, - "Comments": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Comments" - }, - "Contact": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Contact" - }, - "Country": { - "type": "string", - "ablType": "CHARACTER", - "default": "USA", - "title": "Country" - }, - "CreditLimit": { - "type": "number", - "ablType": "DECIMAL", - "default": 1500, - "title": "Credit Limit" - }, - "Discount": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Discount" - }, - "EmailAddress": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Email" - }, - "Fax": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Fax" - }, - "Phone": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Phone" - }, - "PostalCode": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Postal Code" - }, - "SalesRep": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Sales Rep" - }, - "State": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "State" - }, - "Terms": { - "type": "string", - "ablType": "CHARACTER", - "default": "Net30", - "title": "Terms" - } - } - } - }} - }} - }, - "operations": [ - { - "path": "", - "useBeforeImage": true, - "type": "update", - "verb": "put", - "params": [ - { - "name": "dsCustomer", - "type": "REQUEST_BODY" - }, - { - "name": "dsCustomer", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "", - "useBeforeImage": true, - "type": "delete", - "verb": "delete", - "params": [ - { - "name": "dsCustomer", - "type": "REQUEST_BODY" - }, - { - "name": "dsCustomer", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "?filter={filter}", - "useBeforeImage": true, - "type": "read", - "verb": "get", - "mappingType": "JFP", - "capabilities": "ablFilter,top,skip,id,orderBy", - "params": [ - { - "name": "filter", - "type": "QUERY" - }, - { - "name": "dsCustomer", - "type": "RESPONSE_BODY" - } - ] - }, - { - "name": "count", - "path": "\/count?filter={filter}", - "useBeforeImage": false, - "type": "invoke", - "verb": "put", - "params": [ - { - "name": "filter", - "type": "QUERY" - }, - { - "name": "numRecs", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "", - "useBeforeImage": true, - "type": "create", - "verb": "post", - "params": [ - { - "name": "dsCustomer", - "type": "REQUEST_BODY" - }, - { - "name": "dsCustomer", - "type": "RESPONSE_BODY" - } - ] - }, - { - "name": "SubmitCustomer", - "path": "\/SubmitSCustomer", - "useBeforeImage": true, - "type": "submit", - "verb": "put", - "params": [ - { - "name": "dsCustomer", - "type": "REQUEST_BODY" - }, - { - "name": "dsCustomer", - "type": "RESPONSE_BODY" - } - ] - } - ] - }, - { - "name": "Item", - "path": "\/Item", - "autoSave": false, - "schema": { - "type": "object", - "additionalProperties": false, - "properties": {"dsItem": { - "type": "object", - "additionalProperties": false, - "properties": {"ttItem": { - "type": "array", - "primaryKey": ["Itemnum"], - "items": { - "additionalProperties": false, - "properties": { - "_id": { - "type": "string", - "semanticType": "Internal" - }, - "_errorString": { - "type": "string", - "semanticType": "Internal" - }, - "Itemnum": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Item Num" - }, - "ItemName": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Item Name" - }, - "Price": { - "type": "number", - "ablType": "DECIMAL", - "default": 0, - "title": "Price" - }, - "Onhand": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "On Hand" - }, - "Allocated": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Allocated" - }, - "ReOrder": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Re Order" - }, - "OnOrder": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "On Order" - }, - "CatPage": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Cat Page" - }, - "CatDescription": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Cat-Description" - }, - "Category1": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Category1" - }, - "Category2": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Category2" - }, - "Special": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Special" - }, - "Weight": { - "type": "number", - "ablType": "DECIMAL", - "default": 0, - "title": "Weight" - }, - "Minqty": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Min Qty" - } - } - } - }} - }} - }, - "operations": [ - { - "path": "", - "useBeforeImage": true, - "type": "delete", - "verb": "delete", - "params": [ - { - "name": "dsItem", - "type": "REQUEST_BODY" - }, - { - "name": "dsItem", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "", - "useBeforeImage": true, - "type": "update", - "verb": "put", - "params": [ - { - "name": "dsItem", - "type": "REQUEST_BODY" - }, - { - "name": "dsItem", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "", - "useBeforeImage": true, - "type": "create", - "verb": "post", - "params": [ - { - "name": "dsItem", - "type": "REQUEST_BODY" - }, - { - "name": "dsItem", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "?filter={filter}", - "useBeforeImage": true, - "type": "read", - "verb": "get", - "params": [ - { - "name": "filter", - "type": "QUERY" - }, - { - "name": "dsItem", - "type": "RESPONSE_BODY" - } - ] - }, - { - "name": "SubmitItem", - "path": "\/SubmitItem", - "useBeforeImage": true, - "type": "submit", - "verb": "put", - "params": [ - { - "name": "dsItem", - "type": "REQUEST_BODY" - }, - { - "name": "dsItem", - "type": "RESPONSE_BODY" - } - ] - } - ] - }, - { - "name": "Order", - "path": "\/Order", - "autoSave": false, - "schema": { - "type": "object", - "additionalProperties": false, - "properties": {"dsOrder": { - "type": "object", - "additionalProperties": false, - "properties": {"ttOrder": { - "type": "array", - "primaryKey": ["Ordernum"], - "items": { - "additionalProperties": false, - "properties": { - "_id": { - "type": "string", - "semanticType": "Internal" - }, - "_errorString": { - "type": "string", - "semanticType": "Internal" - }, - "Ordernum": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Order Num" - }, - "CustNum": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Cust Num" - }, - "OrderDate": { - "type": "string", - "ablType": "DATE", - "default": "today", - "title": "Ordered", - "format": "date" - }, - "ShipDate": { - "type": "string", - "ablType": "DATE", - "default": null, - "title": "Shipped", - "format": "date" - }, - "PromiseDate": { - "type": "string", - "ablType": "DATE", - "default": null, - "title": "Promised", - "format": "date" - }, - "Carrier": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Carrier" - }, - "Instructions": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Instructions" - }, - "PO": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "PO" - }, - "Terms": { - "type": "string", - "ablType": "CHARACTER", - "default": "Net30", - "title": "Terms" - }, - "SalesRep": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Sales Rep" - }, - "BillToID": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Bill To ID" - }, - "ShipToID": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Ship To ID" - }, - "OrderStatus": { - "type": "string", - "ablType": "CHARACTER", - "default": "Ordered", - "title": "Order Status" - }, - "WarehouseNum": { - "type": "integer", - "ablType": "INTEGER", - "default": 0, - "title": "Warehouse Num" - }, - "Creditcard": { - "type": "string", - "ablType": "CHARACTER", - "default": "Visa", - "title": "Credit Card" - } - } - } - }} - }} - }, - "operations": [ - { - "path": "", - "useBeforeImage": true, - "type": "create", - "verb": "post", - "params": [ - { - "name": "dsOrder", - "type": "REQUEST_BODY" - }, - { - "name": "dsOrder", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "?filter={filter}", - "useBeforeImage": true, - "type": "read", - "verb": "get", - "params": [ - { - "name": "filter", - "type": "QUERY" - }, - { - "name": "dsOrder", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "", - "useBeforeImage": true, - "type": "delete", - "verb": "delete", - "params": [ - { - "name": "dsOrder", - "type": "REQUEST_BODY" - }, - { - "name": "dsOrder", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "", - "useBeforeImage": true, - "type": "update", - "verb": "put", - "params": [ - { - "name": "dsOrder", - "type": "REQUEST_BODY" - }, - { - "name": "dsOrder", - "type": "RESPONSE_BODY" - } - ] - }, - { - "name": "SubmitOrder", - "path": "\/SubmitOrder", - "useBeforeImage": true, - "type": "submit", - "verb": "put", - "params": [ - { - "name": "dsOrder", - "type": "REQUEST_BODY" - }, - { - "name": "dsOrder", - "type": "RESPONSE_BODY" - } - ] - } - ] - }, - { - "name": "Salesrep", - "path": "\/Salesrep", - "autoSave": false, - "schema": { - "type": "object", - "additionalProperties": false, - "properties": {"dsSalesrep": { - "type": "object", - "additionalProperties": false, - "properties": {"ttSalesrep": { - "type": "array", - "primaryKey": ["SalesRep"], - "items": { - "additionalProperties": false, - "properties": { - "_id": { - "type": "string", - "semanticType": "Internal" - }, - "_errorString": { - "type": "string", - "semanticType": "Internal" - }, - "SalesRep": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Sales Rep" - }, - "RepName": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Rep Name" - }, - "Region": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Region" - }, - "MonthQuota": { - "type": "array", - "maxItems": 12, - "items": {"type": "integer"}, - "ablType": "INTEGER", - "default": 0 - } - } - } - }} - }} - }, - "operations": [ - { - "path": "", - "useBeforeImage": true, - "type": "delete", - "verb": "delete", - "params": [ - { - "name": "dsSalesrep", - "type": "REQUEST_BODY" - }, - { - "name": "dsSalesrep", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "", - "useBeforeImage": true, - "type": "update", - "verb": "put", - "params": [ - { - "name": "dsSalesrep", - "type": "REQUEST_BODY" - }, - { - "name": "dsSalesrep", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "", - "useBeforeImage": true, - "type": "create", - "verb": "post", - "params": [ - { - "name": "dsSalesrep", - "type": "REQUEST_BODY" - }, - { - "name": "dsSalesrep", - "type": "RESPONSE_BODY" - } - ] - }, - { - "name": "SubmitSalesrep", - "path": "\/SubmitSalesrep", - "useBeforeImage": true, - "type": "submit", - "verb": "put", - "params": [ - { - "name": "dsSalesrep", - "type": "REQUEST_BODY" - }, - { - "name": "dsSalesrep", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "?filter={filter}", - "useBeforeImage": true, - "type": "read", - "verb": "get", - "params": [ - { - "name": "filter", - "type": "QUERY" - }, - { - "name": "dsSalesrep", - "type": "RESPONSE_BODY" - } - ] - } - ] - }, - { - "name": "State", - "path": "\/State", - "autoSave": false, - "schema": { - "type": "object", - "additionalProperties": false, - "properties": {"dsState": { - "type": "object", - "additionalProperties": false, - "properties": {"ttState": { - "type": "array", - "primaryKey": ["State"], - "items": { - "additionalProperties": false, - "properties": { - "_id": { - "type": "string", - "semanticType": "Internal" - }, - "_errorString": { - "type": "string", - "semanticType": "Internal" - }, - "State": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "State" - }, - "StateName": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "State Name" - }, - "Region": { - "type": "string", - "ablType": "CHARACTER", - "default": "", - "title": "Region" - } - } - } - }} - }} - }, - "operations": [ - { - "path": "", - "useBeforeImage": true, - "type": "create", - "verb": "post", - "params": [ - { - "name": "dsState", - "type": "REQUEST_BODY" - }, - { - "name": "dsState", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "?filter={filter}", - "useBeforeImage": true, - "type": "read", - "verb": "get", - "params": [ - { - "name": "filter", - "type": "QUERY" - }, - { - "name": "dsState", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "", - "useBeforeImage": true, - "type": "delete", - "verb": "delete", - "params": [ - { - "name": "dsState", - "type": "REQUEST_BODY" - }, - { - "name": "dsState", - "type": "RESPONSE_BODY" - } - ] - }, - { - "name": "SubmitState", - "path": "\/SubmitState", - "useBeforeImage": true, - "type": "submit", - "verb": "put", - "params": [ - { - "name": "dsState", - "type": "REQUEST_BODY" - }, - { - "name": "dsState", - "type": "RESPONSE_BODY" - } - ] - }, - { - "path": "", - "useBeforeImage": true, - "type": "update", - "verb": "put", - "params": [ - { - "name": "dsState", - "type": "REQUEST_BODY" - }, - { - "name": "dsState", - "type": "RESPONSE_BODY" - } - ] - } - ] - } - ] - }] -} \ No newline at end of file diff --git a/SportsApp/Sports/PASOEContent/static/auth/login.html b/SportsApp/Sports/PASOEContent/static/auth/login.html deleted file mode 100644 index 8359c4a..0000000 --- a/SportsApp/Sports/PASOEContent/static/auth/login.html +++ /dev/null @@ -1,10 +0,0 @@ - - -
-
-
- -
- - diff --git a/SportsApp/Sports/PASOEContent/static/auth/login.jsp b/SportsApp/Sports/PASOEContent/static/auth/login.jsp deleted file mode 100644 index d0ffc89..0000000 --- a/SportsApp/Sports/PASOEContent/static/auth/login.jsp +++ /dev/null @@ -1,9 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - errorPage="/WEB-INF/jsp/errorPage.jsp"%> -<% -// Forward to protected login page. -request.getRequestDispatcher("/WEB-INF/jsp/loginPage.jsp").forward(request, response); -%> diff --git a/SportsApp/Sports/PASOEContent/static/auth/loginfail.html b/SportsApp/Sports/PASOEContent/static/auth/loginfail.html deleted file mode 100644 index b2d09a4..0000000 --- a/SportsApp/Sports/PASOEContent/static/auth/loginfail.html +++ /dev/null @@ -1,6 +0,0 @@ - - -login failed - - - diff --git a/SportsApp/Sports/PASOEContent/static/auth/logout.html b/SportsApp/Sports/PASOEContent/static/auth/logout.html deleted file mode 100644 index 65aed95..0000000 --- a/SportsApp/Sports/PASOEContent/static/auth/logout.html +++ /dev/null @@ -1,9 +0,0 @@ - - -
- -
- - - diff --git a/SportsApp/Sports/PASOEContent/static/auth/logout.jsp b/SportsApp/Sports/PASOEContent/static/auth/logout.jsp deleted file mode 100644 index bd4870e..0000000 --- a/SportsApp/Sports/PASOEContent/static/auth/logout.jsp +++ /dev/null @@ -1,11 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - errorPage="/WEB-INF/jsp/errorPage.jsp"%> -<% -// Forward to protected logout page. -request.getRequestDispatcher("/WEB-INF/jsp/logoutPage.jsp").forward(request, response); -%> - - diff --git a/SportsApp/Sports/PASOEContent/static/commonPageFooter.html b/SportsApp/Sports/PASOEContent/static/commonPageFooter.html deleted file mode 100644 index 014396a..0000000 --- a/SportsApp/Sports/PASOEContent/static/commonPageFooter.html +++ /dev/null @@ -1,6 +0,0 @@ -
-
- - diff --git a/SportsApp/Sports/PASOEContent/static/commonPageHeader.html b/SportsApp/Sports/PASOEContent/static/commonPageHeader.html deleted file mode 100644 index f02b8e7..0000000 --- a/SportsApp/Sports/PASOEContent/static/commonPageHeader.html +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/SportsApp/Sports/PASOEContent/static/commonStyle.css b/SportsApp/Sports/PASOEContent/static/commonStyle.css deleted file mode 100644 index f3bf549..0000000 --- a/SportsApp/Sports/PASOEContent/static/commonStyle.css +++ /dev/null @@ -1,275 +0,0 @@ - -body { - margin: 0px; - background: url("../static/images/background.png") no-repeat center center fixed; - -webkit-background-size: cover; - -moz-background-size: cover; - -o-background-size: cover; - background-size: cover; -} - -body, button, input, select, textarea { - font-family: Open Sans, Arial, Helvetica, sans-serif; - font-size: 13px; -} - -.button, input[type=button], input[type=submit], input[type=reset], - button { - height: 30px; - background-color: #d94819; - font-weight: 400; - padding: 3px 6px 3px 6px; - cursor: pointer; - background-clip: border-box; - border: 2px solid transparent; - font-size: 13px; - color: white; - text-align: center; - text-shadow: 0px 0px; - text-transform: uppercase; -} - -.button:HOVER, input[type=button]:HOVER, input[type=submit]:HOVER, input[type=reset]:HOVER, - button:HOVER { - background-color: #f54b00; -} - -.button:disabled, input[type=button]:disabled, input[type=submit]:disabled, - input[type=reset]:disabled, button:disabled { - filter: Alpha(opacity = 40); - opacity: .4; - cursor: inherit; -} - -.button.secondary, input[type=button].secondary, input[type=submit].secondary, - input[type=reset].secondary, button.secondary { - background-color: #777777; -} - -.button:HOVER.secondary, input[type=button]:HOVER.secondary, input[type=submit]:HOVER.secondary, - input[type=reset]:HOVER.secondary, button:HOVER.secondary { - background-color: #B1B1B1; -} - -.clear { - clear: both; -} - -.text-center { - text-align: center; -} - -.icon-36 { - width: 36px; - height: 36px; -} - -.icon-48 { - width: 48px; - height: 48px; -} - -.icon-64 { - width: 64px; - height: 64px; -} - -#header { - width: 100%; -} - -#header #ProgressBanner { - height: 39px; - background-color: #1E1F24; - margin: 0; -} - -#header #ProgressBanner #ProgressLogo { - height: 39px; -} - -#header #PASbanner { - height: 48px; - background-color: #56555A; - margin: 0; -} - -#header #PASbanner H2 { - color: #FFF; - font-size: 24px; - font-weight: normal; - margin: 0 0 0 0; - padding: 0; -} - -#header #PASbanner H2 IMG { - vertical-align: -10px; - margin: 7px 5px 0 8px; -} - -#PASmain {width 80%; - margin: 40px 10% 0 10%; -} - -#PASmain #Server { - float: left; - width: 80%; - height: 475px; - border: 1px solid #DDD; - background: #FFF; -} - -#Server #ServerInfo { - width: 100%; - float: left; - padding-top: 15px; - padding-left: 20px; - padding-bottom: 20px; -} - -#Server #ServerInfo #ServerIcon { - float: left; - padding: 5px 15px 0 0; -} - -#Server #ServerInfo #ServerName { - float: left; -} - -#Server #ServerInfo #ServerName H2 { - font-family: "Lato"; - font-size: 32px; - font-weight: normal; - margin: 5px 0 8px 0; -} - -#Server #ServerInfo #ServerName P { - color: #AAA; - font-size: 14px; - margin: 0; -} - -#Server #ServerDetails { - float: left; - width: 100%; -} - -#Server #ServerDetails .column { - float: left; - width: 50%; -} - -#Server #ServerDetails DL { - overflow: auto; - float: left; - width: 100%; - margin: 18px 0 0 20px; -} - -#Server #ServerDetails DL DT, #Server #ServerDetails DL DD { - float: left; - font-size: 13px; -} - -#Server #ServerDetails DL DT { - clear: left; - width: 23%; - color: #AAA; - margin-right: 2%; - font-weight: normal; -} - -#Server #ServerDetails DL DD { - width: 75%; - margin-left: 0; -} - -#Server BUTTON { - margin: 45px 0 30px 0; -} - -#Server BUTTON IMG { - width: 16px; - height: 16px; - vertical-align: -2px; - margin-right: 5px; -} - -#PASmain #Links { - float: left; - width: 19%; - height: 475px; - background-color: #F2F2F2; - border-top: 1px solid #DDD; - border-right: 1px solid #DDD; - border-bottom: 1px solid #DDD; -} - -#PASmain #Links UL { - display: block; - list-style-type: none; - height: 450px; - margin: 0; - padding: 0; -} - -#PASmain #Links UL LI { - display: block; - height: 20%; - border-bottom: 1px solid #DDD; - margin: 0; - padding: 0; -} - -#PASmain #Links UL LI:first-child { - border-top: 0; -} - -#PASmain #Links UL LI:last-child { - border-bottom: 0; -} - -#PASmain #Links UL LI A { - display: block; - width: 100%; - height: 100%; - color: #3F3F3F; - font-size: 14px; - font-weight: bold; - text-decoration: none; - text-align: center; -} - -#PASmain #Links UL LI A:hover { - background: #e8e8e8; -} - -#PASmain #Links UL LI IMG { - display: block; - border: 0; - margin: 0 auto 10px auto; - padding-top: 25px; -} - -#PASmain #Links UL LI SPAN { - - -text-align: center; - padding: 10px 0; -} - -#Footer { - clear: left; - position: fixed; - bottom : 0px; - width: 100%; - height: 30px; - background-color: #373a3f; -} - -#Footer SPAN { - display: block; - float: right; - text-align: right; - color: #FFF; - font-size: 10px; - padding: 10px 12px 0 0; -} \ No newline at end of file diff --git a/SportsApp/Sports/PASOEContent/static/error/error401.html b/SportsApp/Sports/PASOEContent/static/error/error401.html deleted file mode 100644 index fbf9c46..0000000 --- a/SportsApp/Sports/PASOEContent/static/error/error401.html +++ /dev/null @@ -1,3 +0,0 @@ - -Unauthorized - diff --git a/SportsApp/Sports/PASOEContent/static/error/error403.html b/SportsApp/Sports/PASOEContent/static/error/error403.html deleted file mode 100644 index 143345b..0000000 --- a/SportsApp/Sports/PASOEContent/static/error/error403.html +++ /dev/null @@ -1,3 +0,0 @@ - -access denied - diff --git a/SportsApp/Sports/PASOEContent/static/error/error404.html b/SportsApp/Sports/PASOEContent/static/error/error404.html deleted file mode 100644 index 33d17b3..0000000 --- a/SportsApp/Sports/PASOEContent/static/error/error404.html +++ /dev/null @@ -1,3 +0,0 @@ - -Not available - diff --git a/SportsApp/Sports/PASOEContent/static/error/error500.html b/SportsApp/Sports/PASOEContent/static/error/error500.html deleted file mode 100644 index c46a5e8..0000000 --- a/SportsApp/Sports/PASOEContent/static/error/error500.html +++ /dev/null @@ -1,3 +0,0 @@ - -Service experienced an internal error. - diff --git a/SportsApp/Sports/PASOEContent/static/error/error503.html b/SportsApp/Sports/PASOEContent/static/error/error503.html deleted file mode 100644 index b0fabe2..0000000 --- a/SportsApp/Sports/PASOEContent/static/error/error503.html +++ /dev/null @@ -1,3 +0,0 @@ - -Service is unavailable. - diff --git a/SportsApp/Sports/PASOEContent/static/home.html b/SportsApp/Sports/PASOEContent/static/home.html deleted file mode 100644 index 59ddead..0000000 --- a/SportsApp/Sports/PASOEContent/static/home.html +++ /dev/null @@ -1,5 +0,0 @@ - - -Home - - diff --git a/SportsApp/Sports/PASOEContent/static/home.jsp b/SportsApp/Sports/PASOEContent/static/home.jsp deleted file mode 100644 index 6a7da70..0000000 --- a/SportsApp/Sports/PASOEContent/static/home.jsp +++ /dev/null @@ -1,62 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - trimDirectiveWhitespaces="true" - errorPage="/WEB-INF/jsp/errorPage.jsp"%> - - -Progress Application Home - - -<%@ taglib prefix="sec" uri="/WEB-INF/security.tld" %> - - -<%@ include file="/static/commonPageHeader.html" %> - -

Welcome to Progress Application Server for OpenEdge

-
-Current session: - - - - - -
-<% - String lmodel = application.getInitParameter("contextConfigLocation"); - if ( ! lmodel.matches(".*oeablSecurity-anonymous.*") ) { -%> -<%-- Include login/logout info only for non-anonymous model policies --%> - - - - -
RemoteUser: <%= request.getRemoteUser()%>
Principal: <%= request.getUserPrincipal()%>
RemoteAddr: <%= request.getRemoteAddr()%>
ServerName: <%= request.getServerName()%>

Login: yes
Username:
- <% if ( lmodel.matches(".*oeablSecurity-form.*") ) { %> -

- - <% } %> - - -
- Login: no - Username: - - <% if ( lmodel.matches(".*oeablSecurity-form.*") ) { %> -

- - <% } %> -
-<% } else { %> -<%-- Show anonymous policy as not/applicable --%> - -
Login: N/A -<% } %> -
-

-<%@ include file="/static/commonPageFooter.html" %> - - diff --git a/SportsApp/Sports/PASOEContent/static/images/Thumbs.db b/SportsApp/Sports/PASOEContent/static/images/Thumbs.db deleted file mode 100644 index 52708be32ddeb1c1036c47f96158e38011487213..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108032 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 _mGxo?%D^XI;Z4!O^ z4E{p;<>vseVhu`4beC9V`e*nCZEaM6tAoeR2QAr46w?nC!)4$S-s&8DIe_le+oQjZrppvTtqNy3tR!w zz8kbYcqbtx zYbOev!4=>f4J)KTd;vnX~rvSUKGAQHGfZ|}- z7wE%^NUEz5su#XO+%L*9COz`1(gkjD%oQ6FeyW~@!c)^Zn<5MVyn`UnyM~xJWP2dT)1+_OKkLLaaRu zv~Zy7L#pw(T-7hL{WWKx!Eq!=eWKUIEH=`j#%%C9<{xP*I48Oab>WPbVEqwa@}dFb7_RFr|BEb2>;$Kn8S3&JxBi_M5^F36%`5+qa#82I$b79}jg?@meOX z`A^PECFgX#GsByo$zG}aKJXtS4!H90F};OVef%dB-Z1<*im5R}W$ygIB1k^5e2)xn z^B#W-9b{f2YvxsM)wJ{~L{7p2UzPimyqoCD0Jq@6rly5!ZlGLR=zeiY3$6`ZW+^IE zogn7+IGYEiDh+9|2vV%eY~XQ&Xu`F$X@`9yB&(Ng_5zG2Cr6MjrfZ#3@5S+4>=$$# z;PW-KeR&ehJ1o6ee>jhM!$b=aUfq0)PJEMB!qWVDI07MnAHR3!vJN7{G^+g?%eFfyKR}DxRoWqP8&o_Q7{_0kj31k{u0WKa~lv6^BmsJ@)z}mHT zUK}=1Blm>mu+ca;E)8rhHMe~TzQX={L*|H{!9Q_LtwRd0#R617-O<9$z)Z^!R)r1k z4}!YhslRD{$fUgzbnSar=733YyvN&@R8KD)Uy#b*Z?qm)yj4rPCf!e;cc@>hULPRo zypV1S-bIHSH)PdBSYDDYzymUsboprE5 z*t((%QovVU|K@wx`AWTiB>^2f!Zp4RB)--kh`(KLf-qvDci_K$`f~X0KEYQY3o(_5fEXjmU44oS-GzraE- z47NEp0IK^rZSu5bM`|mw;F^XtcSPaI$l-TmyB)r92ax!hMV&yqrm@Ki0i?M%E6+AD zvkR_S@{6oF^*`gfH@Mg7R>byS{el&F^GOk{F*4-TR-fE?9Q#hnj0$MjJ0=xvHY79Y zHg3pc-x4R~Sk0LSY4ki+8}+2uBj)0|WV{@t`-?A}2I~uEP7j3_tcK#7%$zT{2@^Fs zYFz!^U?$gmUAvzJryTfELG7=UMZWyHd5_myO20q;(y`EsDdIt9 zG2x%vj7N}R(c)~ggN(9xND&H#%CrhF&5q+1+Jzp~j0OoKYq0_BzwwG~d)flD8=wlI z4hg4dCnQE%o68EOJsHO8yl}lXAc{_=T zfeGCPv!Je3K^d`d)pXBVDxJAYyIq+~IUmo@l0o?3(1L2OfcvC_o@X5Cc175{dv)5p zz?j#{bx^|2_?p-$+$P~GYpJOz*{4L||KRoXE z-AhAh@!w-&(&0keC`Q+zB}cZ`Tigz&W$Ep_2Zb(3XLiDTSL_re9wbTRe?!>Nf4d2_ zla?Pq4EQP$;{&vYk=&A#HN|o~>ro#}BJvfG_L}f0Jt`Zw8}e)S4{kRULC)#)!PVac zqfFh)d%B+m_>4)PK0Xt7yganFTsQPziDo~EAW*uez*UoWY01>VSX6m8gF!IW@7q}K zcoB54xxESfk=NIq;!07*|4LQZ8{`h-K}vnEzWC5T5?wqJx7#uX{4guN=3IHHbZ$xM z8~LFeCr~P3(|8i^Y?i<j(? z>{FnWLsbRv8zy6b`ipQYmV%7*``^;W1fRYy+jrcKJHNQ6%joxqjTdT%b$|PukerrN z)4eb~p#d3c^CbcI>#t6qL3J8ZxVJCnvD>Xt|O);Bdpj&+)w?dryB( z*S0EfEP5lafsONf;$*2gPC5+P>w4aOLAZI3mwvyy?N2+Igg<1A)sBS}R95J-Tq&xp zMGT4MD0-V%u9Fh1URmF_k4ky!H>aLav+ef@tMY-P$^MKYbJviOOtZp>GWI+|Y18AK zO-R_w(eph-cbjg;E?qeC&39y6Ce_!`WBA@RJysT~5=*hJT@zKYT{^6VO*Q6hpj;rm zz1Rc4qWb}YSOVCkKF3hSrACJ+MPvm(S57c+PsQz*c#a5S*Bif9p|5aV}o z6?*n5#C)}54l#2zFTq2@My_r^HX=2*BI|1Q2X9Y1!t`JnJ3H4IIim|A4EKpqOwRmr z?x)T%F&rUfW40lnY5o_Yk&7{DGjU?`xcMa<^0*#Ab zk&+ug`jIv@WgbZw8naU2ye5Lfy!Mxp4?1%<4i{pv?{dPOaNE^Pch<$yA{J! zD{0RU1xFQ`_fxMI{W&wh`qjLL3D5^>YRd1M zGRiYGPBR^Efl46nU0e>F{0%#Jswh7aHc^;hpUVUQ@$JF&fJwesUqiT~8Si$JV`mT0 zBLVW%!5CamcYTVB#vaJ#FjNvq&<-Q~gR+yRNbWZrnv-S9n~P>h&8$+1NFWoMudJ_$ zWwXHRmpkOg`pm>kB4xrJmcBYw8m0)2kXF4!P8H(RU&r4mEE2foJ8bR=zkcNBQxsiV z<>F(tXEH~mmCW}`Al(ssr9eQc)16Ac+?2fv%YNn552x^%B|oNf&l?=i>?ZlWF^1?o z-~q-fcAH-fTK7djRvAX5o}LVBX*KlB3{4L`N&~(Uv>nXwI?$!{E#PO zWk;h{p4!0nbb^8`47m<5{AtotF}zzd z0Y5rC3>eWCx0x2d_*O-;aLfoQL*rmHGrunoCiCpBV=07qojU*>V#%3JF#s)NW^z(1 z$?>z6Z_=|C?&d8tw3D<~yXqa^{^{X zCT<$}sBN}6)>4gaT(P(JOSZ}B%FVhGa*qPGEuj%h4m zJni&F^FCG4;exf zWg#_Lgd5#!wBu36KXexVnOa)QC2UAP4?CyS_+FY=;o78Bb*KbwX}u>wp0f09mctF7O_)_PZu%OnjBT61`-N>O$Ol5|B2Kf_RAbOY3y?jMQ zg<6fPgiE9_tfykSBnOd`!}TABNu^Fam94Y`s~80?Bv6DmjOZgqt6RcL$sHZ90I!Fv zF0CAuHy>?lLCr=jo3r9~k?)2>Q0#dCt|g1q10=k!%zx3?`i}9!*3}_=e}9n|U4mV; zM)~ENKJ8_KiGf5YGWk$r4Q!z>W2E4q@j6ruMrt|V*+~vJ_K)bV_V}5W`lCyiG%FGO zFrwqL%yp%K)OFD9kJG2Ty1DY+-nWMV=Zkfs5HU+%KA9zav?X5N$Dmna0s93*gfQPC z-eCdKw~z%tiG^MdwjVl>q0i#u{j*^hY8I;+-$B~N55pWQ-2p8AYJo{Rg2)Wz)mI2e+DPLRWe(t43%o6-O+$vY%O|TK6)E#r! zWzk?XmV*(+LtARHP5I7Icg4)X^An;$yEXfj7W5c)6H+0dBnDZc{Jpv($Wluuk3X>H+lOkjS%ae;CKAEKKmsItw( zol5{**03BrqMU3?%+s@BYg%N`p>+t@9qx#ZY`rB5!vyi3?jJjvKx;O1b;>m;GfWv2 zMP%Ji>YrvQexTr8+S}QwYx?e13&$kRs~4Hbba*61`oM^KBH@u0Qm|#PMI+Vz_+gnJ z{aKP0%QyX5|1P-dF$6JdZyWhlw=B1$=08~-4JEW>^yg0sgcxnm=@eV$3BRm8w{MGP zEx(l1<&l>P%9J)99atUw`!n?=9HQLYI_}j78SNRT*rLjRl9lQ~WXGq)(=DyU@6Jn` za-keAW`6)X4QIP<2SG%H@QGvz+8WlMQ|Y1l7D9EP_v0)guD1HNm_eA;_m-Ljd2U0$ zN-WTMxm1B6K*!I)^6H;51M(d3tAM&Nji~=3IoYNu%xaUxNh>{Nf;I86*=+esql?Nm z1yzBP07vYJuMANBD4H_4V%sGge?})Yp9e;IOw66!4up$$_F#z{a!IxkrxFa(pF0*+ zFZ&^aO%d^wfOlG2YdW<=ry!2AL}pFubJ~HE#-4X`O~~nGPRa0(p(nll%H}Ql&kOG zB>t0yY6k9C)YsBw>Ww8uy&)a3Oh6;@PL??^B#k@CA>?3;8Q#s0pr@zhDYeVcbON+QC3iH{Pk@y0r^`J=p56ctw zVoh8u%#7ToHqX}5($WJ_iB76@1x-j5hz!K~bS`Orqlze58lcHmidFil5iSX;V7+4! z4#uoBGmD7}L;=@F!cUZjelC{he)LhmJnPC^OoTll=sTh88RU+4Td-;Z;B{Nkcy%{Aw^ z%NS#>xmva4YglAI4tONlH##?<>6L&Fkf^?Jjs4Nt$g2cRL&OyJu}d;j*sfxI#q0Jo=H|VSzz>{r1Gt4b7voBQMxQxEQIQR@@6_jMuBvELpiivfZ5WJbe^dgM6nUN(g#*}ePci>$NTH^M{mxf5yp zA@;fs7E&NH(}o&kB#-P6@``%_gHtCOGXU>HG>l&+$FURPiMt9#?TuBlAGbStg;zUcQ`F>}RRXSoKK zbm?FAv1F8Xj$*LW5aCDcn=5UC)~3IzM5!)Yv0^MEx3cfPR@*-}*ljV-ls8BI>21|d zih8CT1ICHOe^x{KH%;#l3X!T@2w!zbdOnK$2*p_q<=^XhRI*vy}_zX%VwJ)7w!#~W@ z26c*-hs0pWY*(F3t;gH6EPWw#ZvLO`eam+p8SExTI@TF>&TSRDTU@I6;o;d}hL)dgG1RBudM#W*MHp(7{kC4X(Hta|DKizH z{p$zik?-R?bU%;Oo0dQ@A5FIZ@cpQp>jP;|WiTAJQ`{Euc^OklVyek}Z@2gdmsF4+ zi-#8Lg)GRYE^@tNSXTe_$jCB>M4xJ0bW1$ovPDGZJ9neHr?xa)56+o~)R>>S-*h;qvzXem4AtM_!b>q|BMJ_N<~wcL3owRHkITt7tpfFa zZlQRy-{^~{?#HK*FZ{&FRYbKviwy1gd^zrBsY9wB&9cK1u}Y`R@~EAy(r;#ZX8s7e zES_MJyBplJ^p5|<5qCH6faZb!wYx}vU5ob~Kl@_hlF#IDJXqw2Bmw)T%M?=*fxmRHExx3!MzE*WQe|s{%K#%4N22 z539K?zh+m_nC&=t;SPu(P_<`$A%*=y&DXlig#@UTQ-5~mS~_i)=ebWbDa@-9;-czY z1)f?rDyTFLZ{BijBV%5?nzc_>{IEAyhT;rO^MN8<-^W%}HDuz3msggVTrVbGa+N&m z?QdB~BvG*lx0 za*)yQeOsl6-rx;a38E`EgHFczrF&!FYpXgI_R@0DYCL01CmBNUPtfD4#bBrNcSuFBIjQU|v)-+eanC7l>RmM{_O_aPk8eBK zAqc|!Qv|cZvln~Rp^3pWkY`vB3#tC(i^u3;>jgD#VV{doZ+vuDPhcu4KR)fU&#e5t z3S46qJ7@IR1#8B`vMJBNke@fj+w^#pvqiSr$SaK&moYrDVLCR~i{AQdHF4<%1jEet>Ces+({MdA^)0{)hl zf2sD%3kQc6?#S>XY5CJsf=vf?!=$HuwI?4}i-R6e4>0c@)7{IxtRaLz8|HY|Z0!}q znnVPMw!mz`VTAh2v91aP3G(8>U z{7HO%94No;cG^o7ZeX=+;}zW9s9)-oEuoM@58INdM{geGs#^mut$jB+36XSswUwI| z8MPYvHZEV_qTJJvKJu{}T?6-Kvv{wB5b6#u{RlN77)m0SD66y2KRCH@Pgo)ZHSucW zxo$(a%BN$}^|ui&PkOj2Lqm;}of|#`OXe@jnC)pg9aAVXpUL8y6x_wW3J%2@`WY2| zlX+URLQ7pFql1%>9P*{0g`ie04-c+s-{i9vdmsgBS7~|P4mFOaGwQT^-0M;8b-oO8 z$rS^A4XM%L2YGDKrK>Tj*C1$&LDJ;Tfb2ON<40Y7cZ4q{Z5ycX8zP+o z;-4DTA?OH7T5UNkpe?IL1IQw6WcLzbdzHP|V0h;FQLs@6;&K#9kfH8)Y%$Mv<;2*a zlOgUYMqgXbDpKqh+4w8?)^~Axhg90Lzmuw*s_gbz7{^O%D6f6oh*+ZIZqt{>4--@H+Y#Uj(&?iXmI_~KT&$|#g;5$r$Y8iy~CLY?(AXS z!bT^_AK+H@)zVWFG1kcu!nr$@$5@-t9?kAeQs23>&|bczm#AFf4>$5S`}m~jKvunN zrL=QyiI*%4@M>6ES()?rJ-=cNyr^-2VI3l8Rlfd>nOHr#_T~#AdAAF#K?$StlF@x~ zIdnfSaySU^yj`Es^X4nm&~UoIi4Y82n~G#n!)m)_Rg0U*<}&KIr-^U2o3k^#fx%fr z9~lxYZ7sW7<(coN(x-(A0MCA4J_}_qc$i#cuzWPDa1M``>uuS2QOgC)@+>1&5dXH9{En{U=P4 zDK4(5ogQ2Ie|+8lCZG2I)n(Mk%KF%Gz^7B$tLm!)?^BDRJ+z`GBcq}D`}`&Yrz=M6 zU9GoGttHS?7n?#q1gNjBb)GfTpgR+KKl`q_l;D)E91n9_#1#3mrYyP8&lHu1we$U| ztn{G;(h{TQcOJ;{m<6ZTq!`+IXHHJ0*NLsJ-LoHjmhxmmD78iT_JRCChRpiWMEYct z=dB>aa*a2RYImt*hwoyoXP6>7tSd||1actUCBVO8*3%&zWe$mPmRu7 zUzj27BO3?(Do`QA9Cs|OA4oXIUxeC|6)V!<^=`4#e(;g*-@0O)YJ2gES8Y2yy!rhJ zVokz5Coza^Y(1rf+DcWKsd2M`>!M&n7x?|*d);@rWAAEaxn>O#oX+-4bXEFWzWUY& zzKzqgshZSKa$ixZljEy&+`=3jTG z5|y|E7Pa$-W-bZujd)7%?m&ADlcbKA&x{K6<<)IvPx4Qk<@66!MWVp;S&2JSxhPX| z^K&98megkY)A>Efsi^G6qvmY~|0W9m`zsht2fF^-Z(S568y!RNh36fswj6IG*Jv(K zEYc<3;xl|}b$%Q8DEvF`UNm z?74zhgJ^~d#n+c%ufMnZC#lSo1}|FIlS1#+Y{Y_vWo>ToIo|1UqT{ zUWzW9nlOTUcn|6 ze5h~^9^;j>71rAS_|;{F3cIJbGr9^1+?@F#8*jh4oU<0H#ZPC?zN9pkJ8yseP-js9 z(YvMrI(Vj$$zIdIVxzBoy=KBzDE@Us>B~Np?Znnu$D@oNDyvn^2nNTBuFq}(fr_Q# zcb=v(A#Nsqn}sc^cubVKaqskoD#|Xrw>p3)N{wfAdzXtPwQESn@NJD5ED8i*Bg{X> zs#|>j3e!T~O{-}#my?zI6=+?{wSE^erW=WUqsV0^HklK!dgZCI-p3{U(5*2`8ko|- zc8R(kyKW-ZD14k{ZI8zBIxOqk<)6ES^C?T;XYVF3DqOx^%e@91f_ZJ!{rOX^SNTCn zWkI*yYI;E;UW}WP)`OL)z2QR+t11s`dK+)-_i4HDY+66QzSh|g9#QJ_G&4oitt0f8 z2(uVpZAu_JA+mh6jKqHLw)t%1Ca-Q2b}JrKCm})E!9J9F{%g{C=_@;D4z6qITX&T8 z(cKcx$XmPcB)!A&)vgKMXqOL@AM>pjzPSq!aFPq3JWL*@Z)sc*&;Q}Obb=8E1B8-_>q-FPI+tfeWeEo;S)|;qXq63VxVRn%JO>zl zJrT>g=dpJE!%~wx#>)R$k`-t5yHTs65@u!F=^SI_1duyeZ^PsGmdE)c(M6#J0k2#O zyB0Uxnp3gCyL}uH>q_+4T^Zt%c5@bwopFc}zmM#T&w<-9C zs4;|oBs4+w_*MR<>kIDLO1>L&FFW`tbDqFrn_&;vi>quDI6Bjl7AHD9CZEUWm@JJ| zL05)~K{9Q}z!?J%r-2`=#!z&eJ;+n(we-Kz4JZwSm15PY2YuPe-t_$yM|dBr^nlZT#o_L=&A$r)SsyPv<96 zbN)krKeW2C8!>dXy2enwIV<~!o3()Qysr<_wcK;MK?;X_x^Wa8$FvMrsLsbr?z4Sj zkKoP5+J~HZ@Fb`G>~z&b?$5H9FP}a4je&<^ZcXC0pU;yl@fKq5aGg(DOm$1@ll8yH zPbGCadNMM-JBO+T&c06nb^L$Me~kSz{zaRb)AzpqPve&?`^Wge{$v5Esqynjytw9ETKiF~c|gP& z*GwS1xt1F`!>D=XzDA;lQkK1+4@tT(&pW|*5+NAtd-pij~@~XDK?PIUAQ(z z4%}$5UDU7WyFC3hq_Kcg$?kPqIHvS7oBaF63_O>T-6}n2zEeQmr|#eQ>psd5mdEmH z^EZyZdUoAe!QTo(ha?%bu+Z%>!KYjB<&?~n-^h7 zHW9oPxL>qAwUiS>6e{n;HjZ)fcG(4rbmb~%69jHPO0YR`S~naq>3@=-x^tsE*ne1f zBIIUhMftkbwQN^9gHh-NA!D#p%!cv>*a?)d`c83JCS&Kax!FR)EiS5=s}V@+y)DbE zMaRPrSJVA=57&J<1=c&-b=Ey0(C%kwkg?~N2y5|FN1bvx5*=IseD{(C@hd`z!Jwu? zW3jhg@7)AbaBiB;i@402j4o!P$+ul7ZW-&r;i`_J&KEUczpGlmjn~nV>EjpfE6?OV zSGw?36r-oa(?F3l_L`}lL1uQ~!SojP!i)CtMU9nJi@hLvk2f^>HTqH*e?pCfM=M7I zO^J2~#_nlC*o9%4-$M&fXp)h93${osNrCU)bvvQ$2?kZ))HS%2AyUDB~rA^{t3lsjSRThChuayCev+eAL=SDv<3`mXA>h$+8Y3O@h8_bgO8E3xm*~BSfbzs1DNuRO& z7#%Vx`Auqg`7R^TF7LtmT%luMZmAo5w0KHY7yP^1H`vTmv;;}_R%&IHYnA$4$I)bZ za##|favri+ufdW2aTvtn|1`m^?VRy*<+CbR*X)9P_=1Ox(Rl)tabuG^)r<0lx_Eo3 zMknR{fC|CkJBtoG9-=Bqrjvs@{kL{+u1H)gBlEPndjSplR9kl>35kBYylp`&w2;am zw>z&G{Ha!*g{_wss~{Oa|K=Auxx4p1n=(&G<}df|(95(uGgTAH;!tx97)gFFjDB3! zQst@;;WuY_OU~j<^2@WJXrgCEpmg}SUd4obRlP~IRLdU7-`;Wg;S^&!hqnk{?Q(kM zC~Lj4s-o+&0OM7JeOOsRLd!SdoiH=Zm9I=_P-Q_t5BxfZ>fJNE`z*NYcUda;qs3lq zlij-4?%eE>LNMZ#p!vKifd-kq{d8$-@lLI?FYpY-j?{a0{5FL6&4Po*Du@v5+W5_R zR;>)NSV~1*(&UG(Ab2JM+2`<1eH46j8}{p>+znbYIi@4>kKz?@75FI{g#2bKL~zg^ zY`|0cQIu^XkHL=qV_k7m`IjKnZ6=G3jrlK`sxaQpyotVzMfi!(aaL3+BKflabBAvA ztG6(*mL3ES5?c=*I*b^4RRskf@CMz=etM9GGjYOck4eIvpzs>%H5W+Cdqt5nJSYzH z7)@#N#%0o4)iL3Ka|N=I7IS3biK6Z)0sS3YKjSdxM%j^hzP?a6YL)hkxoGV-lfWmg zMFDZzq>XgJ20>GJVeW*d#&U)t5g)E;I!n6+OFTsNe=L$`(uGm{YCg(qZYy7V05QPb z%G!IXTs&-du&=X4D(6@pevGnS3~>I*H7I)z_)E|>%=$-q%5Fexlh2o8Ws->d45?Y2 zZ@6=&Mi-{$EP@nr$iwzt(HKna=*TVIJog8jm>nRa{royIY`Tl!_aPX zGUV(^&80O@{o*=K`t0VluR8Fi@_Td%NSMnXVvusa4*TSjhbV;+TMu(}qz%AuMNJU@ zcPsu8+pv-J_414AO!e+1pYwymS1Ff$LeQP^2L%dIo&xCY_Z8$doV8)!ywo#S(#zr@ zwXdcGLur_9d}3))#EQ%jQC?0h?`*!1efY}UwD9%BUUq@?a%=*@P1QLaDc^IDiV5+{ zL#F=j$oE6(4|XMBK-YRYC&!yOtwxS7LXRNy)g}WYb5Q!wjb69!*%~8T?DT=yq; zghz5ZSW{zSi|{jSUa%D{IedjHwwBe@p4@(W+v4Dh1t&s=F>*HShqP?F&iy*t9qE3R^TNgu3ta=1 zYumO^KZrqjHha3RgFi_~N%-FYHbO21H-C7+kq;je+f3_HzXh`x)K0{hb_CScW_56_ zv3u|CBcUfJbRjXqNNw`_j6u|2K2Ky5YL3Fh4@Q$QRm;-DC)Q|>j_rYsqV!_HG5DdD z;`b&f=sPg8luPMf2Y(0iq_2OFcDc%Ch_^~lagv#P*8lW-q`GIXO&Z@!pytTsugNq% zxTZ{OdISc3vFi=FL;6ltd}jXK!&k#cBbb(xWIdZUm85POU4yot66l99ad|Gu)VcZE zSs3Po5+{P)ua~ z>zMp`afN{Z9Bb)9@7=oQxqf8v>IcyIN|Vf)c?s#4EsE?p`a;Plmyv>$t+K)9Cv(>li;)2X|DGv4TbE%_Ml81Mwj@2}Dl z!!F&unKg-5LzdIJB(+hL5!C|k#OZh5&k?ugd+uC6`iy+eIV+k?YR5F=nVac0#F*|d zWf1a1c0Y~U1r_3+ZIk%mPonDHOFaa1AJ;oaq#{l$%x%zcEzDg(cil9AKnx z%?`pZT(3&;m3R+u7d7BW)~UJKdVjv`0XSr(%LpDs*`ft3*fz7PBgBAlFi2AOmJ{)# zFjIH>Lj$zUQt=$|8ZoRti0)CwdBRSwmHM{6oV11&ZWoq|P3uTS*gxN)>dg9$;FA5L zu?>DR^atfh>!TT%kX6I}7G5R=8Yhvz7PvcW!C`gP*Mq-`(Ql%Ibp~7Ef z)9s*@22S>LU~R3#K{-s4tfd+fTmZ;Zf@*kgRtQ)aV3?NvQ9H z*5YKxqC2|Qo4h(z!t)pvn73-+JAzUne}w-M#!d12G2?Pfypmm)WLZ*6z%9OE-&~2w z6@Lj_!wl5n^)Jnbpfx2wmk(FqnY_(WSDmX3yp#6KY_NZr7}sUO&ZhIPg+F+YiRqwX z37M1aa*qBd_4>qOs!HQBQw~P4J7ll%;+CW4-zbu=7KcUG>aJH(Uk!+4G_)AeaTRVS z6{*2RbpXDUCRMV!iaGva;>KMc_=T!1QV!oWObf62cTqu$OA)J9zj*$MLvLOry{YSq z7FwLZJ+}l`JJA6An=4bLC*vi(COc12Gw1H>EC!jzl6U{Djj$pY3y5oabc<#IMYGM9 zPso<*G1RqCKxtaOZ3o&UkyK&di6DP$NU5*+5vY}6uw5fnUYpG)n)=h$(BoQl@WFX^ ztemB3SjVgUnsn7;$$yYCgUz`R9RsYI3fF;VcCT&JxUTZT)dQK%_}*j6>%w#BPo||E zHTtk$*muVk8zh+%nQ&>@4>;#_>Du61lC$#HP2IxF`c>F}%Md>BFBs|T$^Mq<6mZC^ z=p}S+!`@KOe{RD%?8zr$KsIQWT~RdUNAaI@pN26~9p%!M$Dy(&6nFM4pv}n5;jFz6 zG*LyLN%_bkjnV2X9PKWP%;yj4uw3{|UHuzE5y(dWS~07~@DWL-X@9W}fVwR3CW#{A zRCORZzBrs>(*tzVYL7F4BHyi)5Y`=O8$tal{hvYc!~#=%&YMe_*)aTB#Mas(k+nF1 zbED$Qn}d5GA$ONc|1-mz>e8=WiSGB1K}-DgIe;!^Sq`_2;ebc4vtU`-yCkD&AH2 zk0OMcZu;=*AV2?lY6gT!xmc%Qr74c<_*b>hPqv8-4`V*3j>6cyONo?xn%dMei-MNz z6)k1A2efE~A5w9m3LNB(LnD+oA4BM`Q^xtWZo?i|u0j@wR(GjzMnauwhQ^+0ca2S) zEiSLj-r^)fp2*qjKjdE_E6PQH|6kKh&!U(*W7&Ydlkh8IKDdSm?pZUURZS zeq)F)+M7E&D?U2ZUR&Q@CmNen{aMT#r;Dj77db9nVK)k-L8p36e63z9$IEGg5R>qQ zmWv>1BzKmaUh?=ByI_;l-?q>A{iHw41FXOm7JFWsxXjY$@wS%S*LF#qY1Ii`EIoqa zY09A~g+_*v)OLZi3f`=^bIV`7yQk@uetf%*h_Nz;D8HhbAMuut{jW&JWMUeTwH7KP zcl;rc{-bPkdfK`gEbQUfX6-hr8=|kzCu6 zM{G9Vo}C0vweY0hmM4x4?BQ876)wei|_qNi!sA z*lJboYgjAg+aLYIh21JBdkk8Ws@;ex9-%1kpRti}3{iWmz|Q2iZh00gY1{+MbXBeT$QfGNFjG+OOjfA<@3{hS38X0%+^}? zG37!`jn%XK>_$GN7?8ii>|Risc341t+GPCRXa`1ZZOfl67j)a%pgs zT6VX*YQs(j9e>0(ATjYV@eH-kB3R|gv6g|6LIkPe&wS2O#wRF@qGPgzaa1o57hx$S zHTBCu_m)suzk!p`JB~*u4rY}SNH=lkj0+#bfmfEbl9aU=wh{Fdg?!&tYKKLT80rEW zTz!qppN#oC2GCM{nQhpF8P%CfVSw?Eb>_*AIpQ67R@u3R;MyJ<&S@c4HZ-if?a(q~ z#)YjIlR{1$)Ur0#^=n#nkn$AFJ*gYypUrT*yidC>Hqn} zp6zK5MGpQdg657zE{9>2_}YCnT2+4yc2RPHLuAtgW-quYlg0KzTt;w;$v90bHVx;YStEY&OoIGDA=VT zxiYEm=L#Pah34;b-)q)iaX--mY}Dx2qOX!Wx?Iv}dDHjih-37^&4v4Ab8ZPtn8K{R zk9W@t15_(?IIFa9RX=D*!-B@Ru?5uH6;=MQleum3#2Zq#uS{yne9QFdAm5DANR;2(8`dUiFzH|+~X}bUUxQX*|I?X_5Ccxq!{HXsXf=?A>W^Y&M|IoN3 z5^0lT$LfPUqxM@Z(!i0zSI37IlccVD4F z@QJg{%lEYvTc^eR>FLR+TDD+=F;dl5JWDa5K?2#qCxE;5_Q}Q5#b>H0w(JFjPxk~h z$3UB0=jWQPE8I4B)x+xM1zXKcZ5fZ$Kb7Py$)P-4gDchn_N@E6@D#LwF#r7{9>>ZRlOnO&{3RY^#EKmLAx-_Jh*@Ry9^ z$8`*XSZUjgx{m`68{${B|1d!xPcxyyf@(zXH&z7s*h2{Xb1`b5n3KrE$qVmeT=d^% z^WX3}xtOG_+b?n^yfKN>!G)jpyVNyNZeMU_6K8 zH0@3kd`r7Kk{)qQvx?dnA`bo05$7J2p5nZFD%_ilZm0!%(<^{2SNH1f41xnwVK-EI z%II`ap4ZBj6+#P@)jJt0_DtGLK;QlY;kR|^4Go59?r>Z4-8e?MLXR{N{u7~&yPB>i znOU3&ds+pb3~J-T8?n;;M8TN0GAX3}6KThZCGb|tk@*jN-aW%Icmd@%>n+9#ET=I? zz8eoNOd$^Ap{y2`3*cGx%hL$_kij8ZfSNIiu=0;ZR(+9+q%1a{44a(Q!fHyZaOxNM z&J+ii#!XPanXNp+)tzKy{SHo>NUTtaaV;dosY;tG5oCoAo$LF;OxU--z+ zt9B9)C|L7P?e`LGVD9i*&#Fpgim#^VhX{S16^STXT?573CJ3qW-E+sFFOP>!Kgk-< zj!SgL>qnHb=8jBs0@7L*j{*HLK+0>k3wu<{4+cObUPo3l7J4L+MPl%{Oc%wOK$gWd zT%A9rHL>nRB(Ug2~N{6E!1IZm;#Qe*CDcN~dnUGsQ33xo<7F{~+FNXmhw? z^@RKk1PYIQIc#vabi0eyLaI^RZ_)c0A$JzctOy^sg~X)kkEC7rBc|uj4_n?hO=wg% z!g=ReWh6$|dVo5|>GH^(BJQt-f$!~gQwUe-)yJqA8*&?c{wh0O@bsH-f~%U`qicQq zVD3soB|X^8jKN_VCSBQQ>!1mnY*bh8g;ow|HBtN2ojRV}wC#Gr4}KzayY(&az za^QBYYubJ~L~!@P-#*Eq_P|}?k9DS9w0?NjM{iaZh;X3%WK(R@{x`m|bj|7BjE7{F z3%jr;%jz9Q==u*rn~m5_5#Hnh%8F#iaw16tQHj2*S$jsAGT{QmQ>yGjK-u6g8<3}! z+==j}iLdq#qXp8nV7wRZo6-Nv{=_yH?)u-_BHbf>R;Fs;tlc>3SSOh(^GuteDRDX- zzsWh=#w13`t!+;}1 z@6oMbu$@ypKgS1SUu%5G2V3H=?mSL{a)UXep}up8J&=+;yF*0u9X)IzX^VXJ+&|ipV_Gz>A_!KV7LU2I}okC z&?Btg)v&#yyajspWZ<6G_X><{964Eqixpk3++D_hnf@KEyDd$ca<`~k%lc|>8fRxZ z1if~)lLv5ORF8RHpagMYj{SHXkX#t=Y_}{B|MDa3eBi)8iZzkcl>K|1cXdYd zdqUv*p;t>ko(OEg+TY@G+LeAWX$my2Wtb&!%0w6E-{H>z5Xi|=`CDXxY@cq9C^*~i z`y_&pw3i>Eu1MB$)U_Bjp_Gl~*OiwQiqG7_LBWX$;@hj6qle>4aa){WLVz>n&;b@p z(bu}q`A&~WspMcGF{h$DxG5piTCS_d#k*DnuN?0ojQj1^?XcRv@DLisbbDi z)Xa}0PehP1W>U!H8U-vjd^uqJ?!@-$3F^#q)yi;M=Pq4?;MIyI&Yj_e>?7I4*^R0V z^#q^mra1OqzwnR$g&AiNLWW|fNukHx?LcK=DT{=4;&z5N(_R{m+O8hpun`@u#uo7u z&8p;DmUeiKZk?t8ZYg`r3CGX2VAdz(o(+H+nApcdTDXC8x6!-DM6T^;=7!PO`WhQ_ zQ&exqJk8cKpxCp+<0FA979Gv)Q&+u7_stX`RDb?HUvymNOa*oaIOqrch)(n;z2b;( z?~_&}6ihT4#!ZA*+Zy1C9F;qJIsXQpwPuaO*{wf{ zG@PXG%%1IRCIAjxrrxrhdfkTni<@IKJ^zD=I~$D#b`uwoCjo}z9iWX47!Ou6dDJ6VWMum!(!1T;nJi|8KMyTzAO^2)X^x>fvi24M zF_p}YZx&Z2^(+wg6ha87uPH)n+mMf?>9e#`qe^Tft+SAnd2aC2lG{A{*ipZ~5Uc+O zu?7{AK-B5Scg%@cPn$WNw~BerE(FZZcf7Ncr`{DS7?5V|b(zD>%YB3}10drL{n^X6 zQwP?zIuOe1Ag<%g$E2?MoKYYGJ>)V7sLeX?3;Xy9ySlXg@iNp_fM?g*-N|j|tR#ui zm-~Tc&}P?8AS(~!7wlkK{Ep8f{sz{K=eXo9I-S8WZ2^!7!Kl5%J5#SMb|zepB0bx~ zU;Jd4`=~2EOefa(6tvpITOC!55E2}{2(5Jed=2Df$AG34q=f=%!RUut<4>jfM>K6X!iz~a5XmMp(I&p4 zA-@IYpavICeTP^V`!aAz=2*^9brY^@fuZTL8mONH#39809;8(p=07U5HGOtGx%mXC zm?tbI8+JBPg)yk3=`TU+%zbg*iCv8`>VpQ*n#KX{grp_P-_olLxRX;1nnRnTN~6Pg z_2?OQ&Yjd3BWb<*gDiJ*gi68zcSZ;D->z!>2HYFnJP{>F?EI#wJ z%>@_?VVvDv8kaY(Yjq7?pO(yO7cQ&8dn*2h)JukUoGFA;gKxxp?h;!C@ywe05eldq z%|&d%nmG)rrO)_?Z*ga!-$IXvYrHmbgeM{l{r&ZrXWwZ=zfgnu@n^=Z0^Z zTmg(Q<#Umv=UIEFoLEpN5%IiaGz|mF(7Pn#78;uOTmdO`Q)XxWO0y8ZmE4AuRgNxW;FeA1s)(EqMUOxiJ}&MG2UU z86ifhSPz59EUVJ<$A1eBideuR+6y>9z;#IxoF@u%~$I1X&xIM?yUbiAb2Es5}-{dqmFbU+bLM@wnyaW1E2 zzG+HT_>{^t&bU(i#bvkS1|V~#GB1;X2yhyodkvvCyS+*In(S~t2GiEb^MRh_doA|B zy~_T+?=XkE;wIbkinFJa{qn70Nz(*kpPT$N=8;-3Zn~WCd-;C_d-WE{8owa38RmZp zpkNO!t`I{_KB4>b(uKfYi+EJseh63)_JKfEwCb!Jm27jaATBf8f7zxgu=*GF@ppWf zWab~I&FKs=7&@N4_(|<^fRWo^JxWioy0K}0czn)d3%2b|*d^uOjcoYg1`$9mW+}~% zf8prC_kUUIQQosxeny+9>hn`D@Wnse0lP)rI4@WNWN~cGXb9JX9yRYZXR1i-GH)8S zp2wa0E2B|Fe?4a1{^Q_KtKaa$p>ZL;UlB+wyd&l?HKr(>MetVd7B}xt&%eat3$If` z=G6I|&s#7{#~VCDbsv~_8sySC6^$ODL4P{|9&0|GPGOKaV5%!T6S|TxSwZ8EeU}XU zQFyYM=Lu;*J~PJ7$glJNaB2zs08fIwnd39zAtEd*z1tNO^a0AmxSrTdfiTx?dLO-^ z)833#>>sBE?^C;!A9DU6*x$A2sEf_;?JM#>$wI20gLkS1qO368S9}|mjSC==Svf0t z6G10saV^%nj9kYFT+cK?C2qg7-F)HXwK@iolw~urICX>9_nBU25hR86qx1+yNe(?B z0R$ZVKe{Zu^kM*+Yi8{g-8+@e42BQdarYykMGXHw$7<-Rb(ZC{ZP?HeZ5+#i`q(ST zI!vaa6nZ3wh0kjrqe7bKhO!lK*XyYNo?jBx98m*4>Wv0~MqEA^J2zNwR z)NE1VW!+Hh+4zd@8vl&qn4;lHj+K_Judm7G7Cv#S&V)_sL};ey8g#!C3AUam3ii^R zd#S3q?JtF<1YrJ0L3wzi^YdJNPT9e3o*1pLRXa)1LUz0tQ+`=vZejG$xneUUEbX*= zeVU||HBBt)+18=`|LEdw{^w^vpjz(<{^8+gw{phqF2{Y&ciC$GfiE28gU8)Z?;m$Q zZa%(mwQH(G>QbuLgjut{WbD$lU>=I7Y&$>zDc=j*T%d7fszik_@UQQx|2Yq&*ieV- zwX*9*5c&%Z37lIWl{du&!4=wvLzp8TmxmB~_z|jA=rlx$T`l{Rp1lCk0?=%J1>veS zGKW93NODk6gQaa5Y9@+xhc3CeUWxZB>dt)+3ETRs0{5*NOohqITaWVHDOR z0CBJ&#wl<4ArIbk2xouZuYEi?uVXNvOsp6_?Ew>gn$zroX`1itUlT+&LF&a@G0MU# zEem=6dm&wKtdC=^Bi9jJvs%d>ItFt&AF(rLr)g;h^vHbwq}PT|i*pO4Lpl%8=>WZj z0J1>ee_6}C*guk>L2A#)SA6ty9wp4xZ2|bNyubJ7D}ZSKuW^$F zRGjP4tuGF!=821zyHZ>SLF+blS!VD+AZN)g`L&LqK^v2jFvUx@;q_ta8tnSo%A3Q< z4+-y2H_Ysf6d`E1&C>|~fy}V3H9M{Y%5@_~$J-pigE|H;lL}*c&!CMJ%C}%r{kCe2 zJ^Z|$`r~ukTQDr94j$>a+glMJku2Az4ak^2O?Pt7r{2Xf_)K23$>j*bA~}7){wp!jciJ%Jt(Eq5zpPFi-$ z-4C6)JWG$|5Czxija~s(?X&>Xchoc5l8`C8Q;T$!6JPQ0kmTS4OW-A3mhE|Sn!8Q2 z#{VL)?IetavX{j{*yMqWkv$reSVPJnv}vHWl{RM$p%1sg%~4OP>*?S#A-*378r%NQ z(V&t}o<~2w4b4sn&N#^9K$E7%kaZJ8>t!#C-EWiIH;@X}stw=yA38}7==_LM3?X2P z39Fi{2Cg>0N63|=kU~JOQ29@;=<^Wn5Gj)#4o7bT^^Sj0Sko5FYMvNFb4*5Lfu`?Mb z*(BkfaUT!ZwL0cTK|x8ft(mE!k8znmH3H}Se7+aMC&E9GC^`l%+Be0ihBj!N*v?`ftS%X(dg^i5@AomtP z3IW%+-zbR-BZc|%;D){H-+Y-+WL{$dj18$Sk}~W~&D}qR(vwpws0PbblDr`Zc4bXY z`4;Sz%($+hH5BqX0SdlvW8R7pA3L+sGF^pyUFEzh2(&qYu0T?~im}PK5TFk~=r02M zfgJ^byb?gK{uo=t$k&Q!8ukU~4O~IRV@T^uBf!mX$rKj-+x>unVSJV;ZQ`swy+2&m zwH3e7MaT|i3E)h+w+-n!e+#wj?&gmGZRx*3^F+v#mv33U=>Xc?lMV&vw4CP3Nd%34 zyqqzrH^t{e1i|2j*W_LU5}tQ2tS_KDbuouMav#KX+Jo88;h8D+>jg?r0P3bo3=TC( z)MWGlNZ71zEE)4NH07xQ0EW$|-bUx=Z~VbJUl#1$ z;xje*Zb)vHkIs`q48RfJJ?gVVQ0lD7yIoN<_hI|e)RszzyqB`xMl;)8y#W5U&3?F? z8;)Y$|3+r^9y4p*%}p8B>XX^|i#fuAo<54vO)1??tK<-9gi1+I?tH$Mus4ueYCS!p zw1L$@Wq{c%BPz12_4t*1Kyd}s(zge zn07Vt*G?g(G(B`pWm+(HC60C*#3FeqLWaxlB6=N^rGc^n>%ew65ca#=@Ywmn5SAc> zy{yBfO{p^YIS0KZ=`M;N^f!K_9^d^CMPPXmn+fc!d7r4e$FJ1=9MM&y4O2mM&2OJo zUZ54Ln0V@u#q7Lq@zPrKlWRQZXHC-EhizPBkr#NWbYm!OQ6V2f$}eZxzI30K*CQRt zd?HwZTrKxkVe#ehnIT*)D>H#z=jv76ok&fD0e}~AGjdqm)oJMT)W#u}rv$M$3Spk-hA(9d z)C22?;m+-r#b06wEjNH|BV~gK`(drBFo-URyM_E?$2Z3;!iLad^|ZWMJYjn@Gv}+7 z5<_@vgs#Dn28?ib1{4{HX>U(`J%x~0fyJxNu_8j+fPYU2(sOrII+WKy4d7L1HVeg193*HmEN)DRJE4MXJIHaKNJ|@HX%9hp|CzPHZIWu|KtEz}IwZrK zx5vB}FZQaQ{&X^;2<$yJKoP#(`Ag7dEi-O5RzAngTo74cz9exrlmSt22O)EkL6G@? z*}jvT>zDY~UGjKtk||^mh71skB&y9YV8z(T`Wi#~)t~CPy2t9~d&M3BH&Q{AYkUCq+6>X$|7lqfx5wXn{N%7Fe&UQ;&qrPQNnMiW;afHexxlZ_ zG|)L2-TYwj%mHp2PD9MF9Sy8$wN58SLRx9F_agj-o)Gv|I%1KrB3jm<2k&`*!e>-D zWYe$1tM!2W3390^P85Yu=k!P?!3zfLgCB6w9>0y<{3$} zhiD8?k)`#%L=R}8V+5g^7OdbB2ceJD&cvkKm?z%QC+$01=y1`O6Fhlvg;_c=e!9MI zBsQL!)C)|dE?9MXhKA;>yZR(V5tbb>nou}{e>#Eeh?5(9=p2`cVAJ?mY7MxL_V$i8 zO789ynnW5ffT-E>1f_0wV~9b#b9`npiJc$(julbh2jnpG&2>irVK7CvADfdIJsFXT z%M8g1UKFSVx~oUij=+6H=OM^^Suda?$fi0;&46jagHS?DIOesR8`MCkTqf6nK@z`Y z0tJNPsS!c2in35Fr@tym4#@=+7Jz|xwjW;wJXDY#Ahi5lKr9vZTcH&$t>*eg5C(qL z!F%yIZXNni0&kV?x0rpkp3~9DbMoUNm z@z$3B%A%udTHW>K=}W?hi?W|-2>cq2chB0prprEpZ1S_3F8k6#7JX7&>w}=tNd)P{ zv~nL!8yo_=F1Ktbb3Vpr%BB*0IjM2Xo6MQqp~<&#x|RCqZFJDgO>f%8_cD%vNx`VQAhITR-V$b={e^Lb%3EOuqG)l^l|TzJIzEmvU6@&%K!URhttG7ik@CEi(x0b&=nLaTmdwDaoRTmbIqe6F$W**@z zPtjM$I>nJHgx==e7siP^dE$x)8PoAc3mq3C3XZ#llqz(9f2LL>4xP|E^%p}wZK07p WeFjQb6L_Pkn&q3*@Bjb)^}hf|B0+ut diff --git a/SportsApp/Sports/PASOEContent/static/images/appserver.png b/SportsApp/Sports/PASOEContent/static/images/appserver.png deleted file mode 100644 index 140c57a84bc90fe3e3bae6f37cd67a2fdbcaec2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1619 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 diff --git a/SportsApp/Sports/PASOEContent/static/images/background.png b/SportsApp/Sports/PASOEContent/static/images/background.png deleted file mode 100644 index 604e33af0dd273b8ba0fabc9d117e50a0c0d31c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 243375 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

L!=so=F8vg#c7im+lwv=V!C7Of)g0?;4ra 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 z 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

?a#m!_S5&)Y_ewBRxws0ng{=rc%5dVye{6q` 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 diff --git a/SportsApp/Sports/PASOEContent/static/images/communities.png b/SportsApp/Sports/PASOEContent/static/images/communities.png deleted file mode 100644 index 7e916f90e309dca539a374b1eef337b4f5ac2cb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3328 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 diff --git a/SportsApp/Sports/PASOEContent/static/images/content.png b/SportsApp/Sports/PASOEContent/static/images/content.png deleted file mode 100644 index 54bdab52997770b2876ae7206e122a218ec0a5ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2510 zcmYjSdpuKrA3rWpM7ofMNogd;HgcIu(_$pIC`3_FB170vQbNl;mykig;SY2;N;HU{V1c3ZNs%;k<4!0NX^!0G#K;LrMjbGJ%}JM|l2xCeN?} zfK>u8UKHf;asVb6jVR}D#gp?0d{8i&U-IyBfoc=vymi|)37MTc zckPnj2imWqazOpChK3Fd1~)J?G&+VtVXUm}9UNU;-8{W6UiA$O3dV;=$Hd0PCncw( zWM)0eFDQIoTui2xSI}O)ZftC7YW~pL+SbKjF#G!ZKYwMjMi%?)jxm! z{QcYOb}X8=!nQz+>kR;q4&fgmKh>B003hyfg+@8KSWaXo;C7$b(|V=oJ?O|=In+rl zmy<%<4ioRoYHU43k|3$ScOxE%tJXvJs@JcMtk&bWTt>i)@F3&18|(S$UB*p+e8SlE zkY_F}B(KEz!Z9-iSlq(nqss>7KFGu}&a4U`GyH8>G-W{g{qPJ?3+l}`h_r8=H zcP6^;K%2#{kFqa*w9{tR{u#-lz!bcMJo{{}wPzbgt&+~<+4()3c7&uSVD)OV4CC95 zCCQvOYO_*T^6Hq#Et{s%3sN-{mj>Ej?TvByX4A2h{;M;nHZ!2n$&{r3GE`d6jN^w@ zSJSNeqF9yYZaV~G&WLX%V%$3mmhQCePv~{MQ#`?yF^rMPIpG^q$rSCA9G@Qk%MQ z%iE`qY<3(;+(6fLwf}T3ILT2(5CBOFpRZx5+g0X}GJS=!?Z>dbNn;_(hn*H`Mi=qi zpAq-_tb-pt^INqxs?4-BV(NoOFt^pmQSjZSmU^zAyc>#)mv?@eh)W%$1xJTSb!3ZYA_ClzZB3PG69`Gn!3I?L@Pk)ZY~eJt-s zKVRvN1-o)*ElC#CFX2h@Fa6Kvyab(k5no#E>L+7D3o0|p0#h7gL2iEtavfwjvjM2&~qdR(7h2m&r_{*^yAG&K0P| za+8%fx#NricYAd4I$HPz{4)7_SlYswi4@;)^;{BTw=@`vNcACi^*YHBGyG}3@x_NB zcG`h+MMt$~?#9vK9i=}q*`d}dDQN(D?MLU!4>f;Kp%#3jT`PPIlvMQOKhMvvKstK5X|bHI zTj%F~YvK<4CPc80LO7b1)MNhWfW}bW*cLUCTvhv8zv@QzB@Lh263LYke^h)4s4J;d zj^J8QrvK*qV{+ANB{7sH_cP=|hXsE6fq1ni$iZ&Vr2B<2Cf@-|3EWo%@%>#&Ym(NY z^k<8RRrP<4WmXu=)}`)hg*314(PpZk)=)PGWXh4XHqrWp#O#eN`bYcas^sxtXgVoL zE^Pzv_H!}!BsImm%HC&jprz)vlKoTy>wHWOJX~_+tZx64<&53+Bi%hJkY&82Mm6IC^#BEPBpw{jKJtP=8EX zqqbPJ=W#jwR3dKjY+~bVWUg2xJelhujWHd*cVAzfqq~dt820%ttl`Sdzq&F9*$A_X z$)>+C>Dd~z*6B{89e3pM;~w|UjB97wyltbfrrGks+^5lT*FC}{cXJm#L0^Y7#XHhI zjsLAjzZ|D4!J0qMKVN6EMqeqn4cQdM&Pm#8cUz~h$KJTGTQ!@WKKY$&;Vxn3>9Elq zr%(+`)|YlR^HLscnr5_K^xkni=#NLzKWd0u)E|;*Q6a%OE;F7>quFW;D}653tT**f zkk>ExyaY2IyQ>o}j@krdi~VX&Z(gg}A?sY5p@#*|_0H&LL&-DJ@96To@K=~hntkFP z+y&dDrGf>v&YQ?lyvam4J9fzU`Md%0)I+a^pl5b<=jJO2Vb&z2r;qpHc8uQ2v~woi zdWt=siQgM~y!F9!9>HBKd~cy|g$|M4zh+2VxMtS(>}$zeY)FIgf95^AuH3E-BGZI5_4dOja#XIT)!k$7?lej@ppn25C^^G@LWKyjsAZ>sR4WO%5yXqUKKGTXM{FwZR8i zL#~%E(Fg_zwY@)z>%ocNzv|gWaPUku)7<(2yAJG_iDP8Sw8{OAJpJ?O@t00DE}HA? bL2m%^b%d*xsE-`p&ls>eVT&#|_q_XW$-j($ diff --git a/SportsApp/Sports/PASOEContent/static/images/documentation.png b/SportsApp/Sports/PASOEContent/static/images/documentation.png deleted file mode 100644 index e69996a24b0a880ce538f4189a706e348bb1c088..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1201 zcmeAS@N?(olHy`uVBq!ia0vp^0YDta!OXzGC^7M(2#~{A9OUlAur|m#Z^_mEKB+I^(p_hFi_EjK9Q(I?^AX}Xbbj%qqa~3#9HRLb?GNj!^)q|6 zwEFg~6*8Kv^WOW?;u??oxJg@oD$81`I_=YM+;06U#x^s;^5y*b4F&#pS6MOa|9CXP zlH>ABiKUJ|`l7WiSg)Eb%aj-=8pIsD%7TS+>LISLn~ur~_X&u{ww%znk8fj3UgCa9 z_|>-?i>?^?zc{?tDR5C$!OR5)&Z=kU>L{hWjLehE_jU2O)X3r#a^s0v%jT6WnJ?TP ztUTiBVR6O(gHYOr?#s4+_bt(QakNhTndhejc8-MB^dPSsbFq2F4NKRlsvhyqdu5dN zv-e)Q)1fyl{FyC*OWd@tbc=mUF*_u%UB^bQR9H{IJ+{R}Mm*zrliZ17TlJ8B9bUPt zv+P%=91~u}b~A5B)bp0MG<(t5=o<{{RXDg#$8yfm(^(TR#XL;ha*Kv-?3!a-?|Mn0qDfV4%#8$PpF@X^3#ltUWVD+wRYaxb`mV2f%~$;ORO7fA z<_mP6h*@TCDS2sT&goaLHaz6uYHHA@a@vmvp`;@GI zcBnQ~c#`kq*K-Sms@$#+%p?YvcYMYh>2&z9W&I+J6A?NTk_vc0@hQxCn9=b66IFlO%m zD;tX9kFP%5W_|rfsrR+;HeN$E90vp4C-{0EgS Mp00i_>zopr0BSz-N&o-= diff --git a/SportsApp/Sports/PASOEContent/static/images/gear.png b/SportsApp/Sports/PASOEContent/static/images/gear.png deleted file mode 100644 index 4937022abcbdea6a62632c2dccf75c2c13a30f3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17253 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 C0+k5> diff --git a/SportsApp/Sports/PASOEContent/static/index.jsp b/SportsApp/Sports/PASOEContent/static/index.jsp deleted file mode 100644 index 7fd23b4..0000000 --- a/SportsApp/Sports/PASOEContent/static/index.jsp +++ /dev/null @@ -1,8 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - errorPage="/WEB-INF/jsp/errorPage.jsp"%> -<% // Forward to the home page -request.getRequestDispatcher("/static/home.jsp").forward(request, response); -%> diff --git a/SportsApp/Sports/PASOEContent/static/serverstatus.js b/SportsApp/Sports/PASOEContent/static/serverstatus.js deleted file mode 100644 index ace972d..0000000 --- a/SportsApp/Sports/PASOEContent/static/serverstatus.js +++ /dev/null @@ -1,78 +0,0 @@ - - - -tryGetServerInfo(); - -document.getElementById('ManageApps').addEventListener('click', function() { - document.location = '/manager'; -}); - -document.getElementById('ManagePAS').addEventListener('click', function() { - document.location = '/oemanager/'; -}); - -function tryGetUrl(url) { - - var xhr = new XMLHttpRequest(); - xhr.onload = function () { - if (xhr.status !== 200) { - // Let the caller handle a null object return value - // throw new Error(); - updateServerInfo(null); - } else { - var jsonObject = JSON.parse(xhr.responseText); - updateServerInfo(jsonObject); - } - - } - xhr.open('GET', url, true); - xhr.send(null); -} - -function tryGetServerInfo() { - - try { - tryGetUrl('../server'); - } catch (e) { - - } -} - -function updateServerInfo(jsonObject) { - - var apsv = document.getElementById('apsv'); - var soap = document.getElementById('soap'); - var rest = document.getElementById('rest'); - var manager = document.getElementById('manager'); - - if (jsonObject) { - document.getElementById('OEVersion').innerHTML = sanitizeInput(jsonObject.ServerInfo.OEVersion); - document.getElementById('TCVersion').innerHTML = sanitizeInput(jsonObject.ServerInfo.TomcatVersion); - document.getElementById('JVMVendor').innerHTML = sanitizeInput(jsonObject.ServerInfo.JVMVendor); - document.getElementById('JVMVersion').innerHTML = sanitizeInput(jsonObject.ServerInfo.JVMVersion); - document.getElementById('PASVersion').innerHTML = sanitizeInput(jsonObject.ServerInfo.PASVersion); - document.getElementById('OSName').innerHTML = sanitizeInput(jsonObject.ServerInfo.OSName); - document.getElementById('OSVersion').innerHTML = sanitizeInput(jsonObject.ServerInfo.OSVersion); - document.getElementById('OSArch').innerHTML = sanitizeInput(jsonObject.ServerInfo.OSArch); - document.getElementById('HostName').innerHTML = sanitizeInput(jsonObject.ServerInfo.HostName); - document.getElementById('IPAddress').innerHTML = sanitizeInput(jsonObject.ServerInfo.IPAddress); - document.getElementById('upTime').innerHTML = "Server has been running for " + sanitizeInput(jsonObject.UpTime); - } else { - // The serverAlert element does not exist - // document.getElementById('serverAlert').innerHTML = 'Server status - // information is disabled'; - document.getElementById('OEVersion').innerHTML = "OpenEdge"; - document.getElementById('TCVersion').innerHTML = "Unavailable"; - document.getElementById('JVMVendor').innerHTML = "Unavailable"; - document.getElementById('JVMVersion').innerHTML = "Unavailable"; - document.getElementById('PASVersion').innerHTML = "Unavailable"; - document.getElementById('OSName').innerHTML = "Unavailable"; - document.getElementById('OSVersion').innerHTML = "Unavailable"; - document.getElementById('OSArch').innerHTML = "Unavailable"; - document.getElementById('HostName').innerHTML = "Unavailable"; - document.getElementById('IPAddress').innerHTML = "Unavailable"; - document.getElementById('upTime').innerHTML = "Server has been running for: Unavailable"; - } -} - -function sanitizeInput(str) { return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } \ No newline at end of file diff --git a/SportsApp/Sports/PASOEContent/static/sessionsExceeded.jsp b/SportsApp/Sports/PASOEContent/static/sessionsExceeded.jsp deleted file mode 100644 index 056f6ea..0000000 --- a/SportsApp/Sports/PASOEContent/static/sessionsExceeded.jsp +++ /dev/null @@ -1,22 +0,0 @@ -<%@ page language="java" - contentType="text/html;charset=UTF-8" - pageEncoding="UTF-8" - session="false" - isErrorPage="true" - trimDirectiveWhitespaces="true" -%> -<%@ taglib prefix="sec" uri="/WEB-INF/security.tld" %> - - -Progress Application Server Error - - - - - -<%@ include file="../WEB-INF/jsp/errorPageHeader.jsp" %> -This session has been expired (possibly due to multiple concurrent logins being attempted as the same user). -<% response.setStatus(401); %> -<%@ include file="../WEB-INF/jsp/errorPageFooter.jsp" %> - - diff --git a/SportsApp/Sports/build-output/AppServer/Customer.r b/SportsApp/Sports/build-output/AppServer/Customer.r new file mode 100644 index 0000000000000000000000000000000000000000..461e68ece6584479d953ddf41188d88703a9e31a GIT binary patch literal 47261 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 literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/GetCustOrders.r b/SportsApp/Sports/build-output/AppServer/GetCustOrders.r new file mode 100644 index 0000000000000000000000000000000000000000..416cfed3ac2fd63931088a9c8da545bc4e9e144e GIT binary patch literal 14664 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 literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/Item.r b/SportsApp/Sports/build-output/AppServer/Item.r new file mode 100644 index 0000000000000000000000000000000000000000..aeecf8c7942049897c2f237023b15fe59f3f6a43 GIT binary patch literal 24343 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 literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/State.r b/SportsApp/Sports/build-output/AppServer/State.r new file mode 100644 index 0000000000000000000000000000000000000000..646ef7789aa4ef93b7c62952f5e8dcfaf23e7470 GIT binary patch literal 16295 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 literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/UpdateCustOrders.r b/SportsApp/Sports/build-output/AppServer/UpdateCustOrders.r new file mode 100644 index 0000000000000000000000000000000000000000..4502bac929754e43c1ab551707922e7310b11bf1 GIT binary patch literal 16797 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 Z6fl$SZ>yiL7D>Xlc>4eS7qWf{{s2Vk=ivYV literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/asstate.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/asstate.r new file mode 100644 index 0000000000000000000000000000000000000000..f519bdd963b52ccda6751b522bcf644bfdaff50d GIT binary patch literal 3161 zcmb^zOK2NcaGvZoa+5S|8j52=cy(zot)s5pCOAE$Ua!{ng0!n>SHS@lqTsb1YFk2* zjSu~(H(v^d_F(9xr;=M+C^XQEp_d#=350@A#n7fWr`+0Hf;#i|J!x0owFzYKzWKbF zH}mH6tbqr=U;kx;`GC+5KlGnSLmFU~5f9)7C}!P?k&$TlIDn>&O=-B`cWJG;aB%`9 z1Xnc3>z2)p56Y#6UvHG_m8G@ikIRdhjgO6vzR!)dMzgx&*Lk3p>G0a9#_>-?y7>aYF5YbDB>9jy+CnnbQv60lkt=Z$E)<=aqUk2L1&^GUbWB&yfOi?B&X4~3$-^(6 zp8J0K+|twB7F+po>*SYb*9U+5`metqe|7h=QV!e%1y`~dd}O@$pE2Iqei{=1l{cc} zAW6O%E|N6-`NE_5;dKD}Dhb$3^t_~#0%F zce9o&j0+APlrx;HIjzr`IMO-k1Un1EqKM^m37S$&txEVsiH}K)6Dj04cLL*d3XHFa zz$YX=iI~oKS>jVt{t9C9_p0PiOZ=L|0LS1s=4J@a!<+CnybG7%Don#Qn1heNhbq)j z6W|F3Og<`?g-?y7zwIu4TLyD{-lKP{XExK$N7q*R0;#vrsyN!~}5 zGg!evq(#Swg<7g;&RC93>QrY+Q*WKAN>rmLwA)cW%C!-q5`3mnz+lHYySN>`=Y zFv`DB(`Y~vfrMqs5C`ybYL@7o zKMMMKblyV))tC1|#?((LZ+lWF+)t#+*f6f${L(v0IZn!NlDsc<2CX*GA9W^XK6MJ$ zN#sWaf6z}_`q{?)uK(A`mOe($*+?h;K6X8#$7Lfh7PaT7^#*>7y@f09*yDc>X@Oz{ zZ_Wkv`>{)uTpe8c$qCl-vcFha=7Qs^l@*^aRO`IC$;mrF47f6 z3tl{U@FW$jc&s3J@KPv9Pf`%+%_^c)5D^brP*`WO`;y(wrd9C^^WJOXtt26NREG>r6}LCz!08s-j;sil&;-b%hz1nW35$Lyy!W6^#i~9W%}p zv~FFDc6J^V`wE)q_`6A!__`thdJC=x1xUvojgZgX*#T6K!XkQipq5hPSgiL@SCmGC zVGYCPn!`MP+oFfAHF?Me>eoNW?N8AJjnj*t17k(b%kK6aUNv)fupi9I{n@0_xC}&J zzF>Gg7eMqGJFYG~oo&1IF1HkYbba~G@{tGcDj(d`8g9{Ydd8U`u+JDWpYN%W4^m6d!YqZu|JW> zCI{r4lFa1Nsi9;VyW>MA`%)QHs7<7kl|WhsWu`Kto{RfB0AH>9GTLD~K8fAPIq-Wx z-vppHupVeT{G2LV3mM$V9x@bueVg6&a0=iimLE<{Tlh!V_IiOfBIqw^c{X0qSU%`L z9`~HX4Dz=q9P?ah(DTr+IqfOI@`MQj4xXO5^7^A2B%?DJ$5v|*VgBro_5I&JIDQMe z`gq&6-2A)-sBWlcYVfm~9l(N>Ctf~`WD7U&gK#;&0h=c9AQHKcY!J3V4C43`gbw8U zkvrH75rNj0VdWxz{xhWuj-96u+J{CIz;P8hrLapSrj0S!i9wH@z-lW%lSO2P8pHs~ zWJRlQ_-y1kjY2E(f@UOGC}mO*^zONx_-(SebzWN;c|50vy7O(1|EK@ZN_zMW207da literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crcust.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crcust.r new file mode 100644 index 0000000000000000000000000000000000000000..4b0b00d9d400a6256251bd066d4d512a4173182b GIT binary patch literal 2319 zcmb_eK}-`t6#ZMPfS@80C6bs%qcIBD5;4UCcG)h_q}{FEEtX^r8oD4+O5L`Ei&Bk= z965S~L=%sA(nJr$c)*JxAtu~J6O3Lp5e}OAXLqLEmaS;wZ)V^B@6Y`CGk<2cb*t|N zUruv6ptxbt-B1g)00SJc0Tc@yo^uWyq1I{uyk1|g7a!QVa@o;#KQctRy%6C;l1I3x z#WT8*(TwXia<^`3W8+y(PU|T#Ii}0$jHaX~jci8a(`iLFrgTHgm_(McZpnam|gZ+5@nJjp%7yVFEj}(3PG={SwTh*MnL} zk^TOz?sgyQj2>q{QQzAu>boNkR)?DxS{q&YHya-} z@2!7fJ*{c1iZjjucg%O?AM@S98j{kmeXCZcYW%E2k)EIWnmj%QAoY+HaQ)@$s20iU z)q?pv!kBlV37(=Z#1&PFMC+msDO*6+SE~ zF)=F0k%%a%iVzcnAvGL|gw$R>%u9l(im`|i3@PT|sEmQaf*ch4_=Jd8I!q_QlZ9!K zj4>4;TT66;yD8%RGHrZIn27Hh6YD7MrC3j~f#NBy*4hPUdN%c!(lE z4b&rh6pq0OI0NV40`x#1T*k|P6?B*c1AAIR<{9B)bZ0d2laSPVXWnavG6hY%&6{)Xx$oWgpK0am^}dxk zi2+(UEPJXepaLK)kq@BT;PPB>(MVd`2jKHHb@=dsr6rRdZSbQ)=J!KHR-@ivYoa&B zlBq=U`gmsYMq+FtozP4+t&EH@%}gb9b0(QiC1lgoS@I@JCQ_Er>}&R=lVhpGaFR`# zji$G$zW#z&nKCD5ZZqb!D>deQpEbe_4?v(C;3XE~CeExMBQ<=6XOUlmMT~lpsk-L( zw_I#!Lhfq6uvr$<>}G3@3Tt~D&MtKIFLG~9Q3hpO1&81xpjc{EX_dbqG!QMWwLA3 z;dmA?=RK%_C&+tb-H6H&rBjXDL=J^$!#FetRbxOmWJ8H*2t7e9q=b~1L5PZKs9H3v zMU+@riz-*Nc(+CRa&(3;9IN~j)E6X!WlRZ zZ4iVW=miF|a0{|n3xIM_zjUW}vLhH2`%c`=O6hKOH@`hT{%9WHE?y_>+T~=pwQ|#P zIX6{kIW)lqW~N8j=)^P|5rP8)j&)c<{evU6>fmlvG^NBao zFj(Ar$$2q|en}s~LSu7UzHctxN6d0gZ?@t%g_f{L Y{00<8N)g9;BDcR{f&cWc!AcK*0LF#~p8x;= literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crintr.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crintr.r new file mode 100644 index 0000000000000000000000000000000000000000..a5624c7fc51b14376c2a3ddd7a83ca2f32120bcb GIT binary patch literal 2211 zcmb_dO=uHQ5T1=u`y(hQSP)@V{KK}S#bR0tCT*8y#bjePscl`+(rjZQ%&g5aP`4zIJ!JX;qx?c7DE@dGp@9msk7l zP2Y^ZOAiq4A zn>}}1Jl`?@oH<+5SUqPpAHHF|Z~wC1684Zbr8nd#6({@6-~&AO#Ey#CB&qRETG&aCh&VEJs_rqI7+5ZPc$u% z|3FemN@$c)Jwl3sX)!L~6jUx%Ku4E28+emU8Sjh9@%Ed(_#XnarM0%b&gS*D9iSd| z!(QzAVFk#R)s;|71$<)6Cnr(7YMyoS@Y>V;GPs{2uX;gN z$BU{qtU?3EKcgfL;vi5abMrCGx*sb~8Ww{01-v2WbER literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crinv.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crinv.r new file mode 100644 index 0000000000000000000000000000000000000000..48ac5e5df3b03377e036f981f82639b0c3774ab6 GIT binary patch literal 2125 zcmb_dO=uHQ5T1=u+gkjA9t5Q>qEhJ(NogS}#jeScteD+2yD1SCRGQZ|Xw#UVl0$2A zvq$kD9xGHRilQe4FFkrHf_U*_FKT=BpdOTU-tNA1)4r`1oG@=@zL|M%-kbNbt?ySy z-_!&GNNw;U*xmvy011KG0J-)^$X86QND6Xpf@sCEsTZe9m9i-ni#jRIkdj%pf&Nf`s8X6Jn`5PX zVWzJrhI@JjM5Qn@ogX8j6Vu1ckXv6Q2(Y{t>)HoE^&;*pOMUJRyHULefZk5j6kUr% z2G8||X#|gcV%qv1o~Idm>oG(B5YNK9u*BU_00O=V2M#s60wS|(?)DwK0WuT)d*{{z!pSRTR51g8toz1LV80wfCiBFAeuwZ8y zr`HVQ$iLS6ZkP3Dv4^xZ?A-j?OAtpd9lB~JwCQH4{#T+EiTRJ056M&Sr!jvn1#f2CaZ-U8P69;jsYGy>^X-; z#MeB&6_3tSSt@T$YhI{(as&Z`U&EabU)&@Oom=zB`ep{tpZ>A4|NCdhUq`V$-?HlN z{Ivg+*?mIQCI7b6;nPbNMdc{+K?bmcaBbEBi!R`yVDJ%IDL4!f7{;FYoJt5XhcWOZ;qF%5Z5C%3DlFh<}g1}!^*jZuI=of?aD zKuRD_+S>Mktx7%Dk!fXKVk_4Xtho;Qzrk<{-9r}K`@UJ36WlmGG@Wn5@PGOiJM;em D=nUwy literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/critem.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/critem.r new file mode 100644 index 0000000000000000000000000000000000000000..032fa921e13e261de13511461994ac4555eb63db GIT binary patch literal 2247 zcmb_dOK1~e5T1>(58K)zB1#dqf}+^gq*#m!nl)LIg(Mqy)7s)fE6p}7wK3))IVjCZ zd+<;^^&nV5q2B7rgCZV1Nks(lP>Z10s|WQU#+iMjf6`5>;4Jgc%r}q!G5_v1zkNOU ztjOtr)B;QH)+T5I2yrYUXM-pVNd)M%A;|^{kdNfId&3C!bB_w6W}D zUZ3vG@ZRq3OV1U6w7Qyub6b>OS&)uwN>IpC|nduywY{ijzxb$0EG?!S?`bhqtRs`hkQfusUtJkJ__^d;do*r6DuG|NBVH_(Ls@_MvN@i3Cu4f)= z=Aj*CQI`^8G&T@5gb)=2LLx4N#JC~_NDqo?K#}4JNsh^}un-Fhk%)}A7*s@A35p8h zBnS%0%COf~`qa1>kc7xZQ3{05GhaS1?2N;>g&i}AfOaQrG5 zIDT6U*=)%jmTW;u`K^|0v*b>cRI44C>UJRSLI!9=Y!7t85jX}ue1*@0j4$>G=rCpY z0cZ}IkKWnMh~}{SO1cx(!<}M(eS6gTa0cKu_7$gg=)6;w6IL!ep*B`VvUOV)(n)=M zGOZ_VfRTRY92O8?WqdCg-AT1mo#rgha3v;(<1lz}_2#=TR+5a)+C2U1dWMr<`LVhG z`)AW%La{NwTrOGlssF@ucd8pvZK1nDCHPqR-Viv5TemW-dQ3NdT8osx z5C^UjnF}-^g^?!=ZR5ahXB6GECYFpj5hx)SEeCz6Y@DDuWX`(j>nk&ZE9Zu$`8V?a LufM-Jfi?aClaTjZ literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crlocdef.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crlocdef.r new file mode 100644 index 0000000000000000000000000000000000000000..575ec930993491379d70e49e3ca3808b38c961d7 GIT binary patch literal 2223 zcmb_dO=uHQ5T1>(KT=TfQV=u6l-X zA7@#9tjk#_E_QXbjPJLlvQ}^jMvpQ3uz~zX#`m1Te$Wl|o6}n5>P!sB;ow^R*1IoG zkc7_WIBmNT;pdls?C<~nnfmLf_QqRQ-N{e-Pabks$eC$~*J|(pb@=d*wP(;E=hPN= zBOKH1fJG0Z!{U6wyNFY845BcGKaM+#b`RQAjzW+lbtSc$>3(vO)n)sPlM&5)I;(&? zj(o1X#+;(vsA}cB2HhC@gd|{ugFu~_n+`XPT6b`>VwX3MPEsQ)o*K|`2cNn#fV_E{ e{lv9_59i#3My15V>?>mWl^OegUwL~5`u7Ld%H7qTehIam(0BPy}x-gZ>HPE*Dpq& z&PygBwZNLcxd9pgh9$xP%wZn$qKA%D{TLkx^b7`Y!qHbOOmziOLlg``EUd=mNPjAs zH?4fix|}J_UP(>Q6jEBw%qr<=Q_JO3EH`Hr@~Lnx$4u+GX{GYEqBqbRC|J|^)TA|M zrMq)-PiN;DS+mlnB|9G74hXnvTlWJj9>BT|0+9R?ceYG+zQYa_2S7&aag9x7EU2%M-db zq${lcb&#v_)rRlH63A7`jUx|>7t34guV#(~9;c5cZcN?GN8b-@_}*=1L+9^edmM6> zak?#Z9(>Pw=^FeEy~Q3o2l_?}f!n&?1x*BJOo{Si(7H0{~Q1lpM z;Sq)DN@7qQ88I|9O2NeKY|IE7Np>NuE5llnDYTZZL{$URMe$}~SyVBWVsny7x1V!- zfi|W_($V9ugv}c{Z{qwA=gr6|M+@hNxxSU-HjXJ%E41Tn>VRH22O&@(21yu)3>46Z zh;q|<^mcb6$|?FwvKQ6Ud-?wQ_Ta?*1%NyFY;b8hj(??L4lF)f|>(-l3JV--hZJhbJ65(anf9!7m{?W0Q zknM~wmrFc9^`C~gmnd@bZp#>2$)BtgQ=w`k^b;ruf4b`xszwz(KnXrPa^q11bkA)P z2hE}AaJgoAzyBV3B%lp~c=tmfNhcAXM(j`%_$8_At=@<4h<4($ zk0pqC+U)?L_uKj9?fiH;c15CYV~m0Q5$5wHW?n0r*-6vRv+69qJxky#5m=!KX&6Vw ymUd2}VzNx&pk2AiS(Q!|Uokh(Z>rFfY6*wL-(saQ$64%vkn`OP{ipvaPX6D%TmR4i literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crordl.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crordl.r new file mode 100644 index 0000000000000000000000000000000000000000..36b7d00fa9fb1334859704f88c27bb94786e7089 GIT binary patch literal 1775 zcmdF_6#>Rt5$G14AbRAOXat zr6p;)7C;dYury%sb@cR8aJGsGElw>ejwwpaD9y=^NzW{a@h?ctb4^K4^)D!n2`p7{3BV6I>W3jZOyy`MU?X2IEc^ z3Ml5GYscg>GAN)3%3AMPdEekP>(6D%5%ZgOi?c=__FL$lujBfQeZFm}dNgTwH_vLVbNa{ak^RXNapWkO~O$bao94iFEM{cJ>eT3vrDIarJX? zbpeZmwSd$Ihd73WLZcSsOpwi>WC)^R7?`^mK$s0G#tx-HSqUW03B-^fjNMY*#|Gxpre2~hJ zh+qbIxroF96?W)FIU@s814s`HcmNp%Fb*C%0VoGb2gm?nCqlFV$^@l!Z1RxO%7I$7 J1j1q<3jk^E(u@EA literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crpo.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crpo.r new file mode 100644 index 0000000000000000000000000000000000000000..7e1dd6509e02234e01dd7f2dec386fd8cd6117a7 GIT binary patch literal 2129 zcmb_dO=#3W6n#y6Wxnxx5U#kb79_rCWtZ|2QR+Be>f zzgXl5Aa%fM`e+-p0gQ4~2T)xW^S>N}QK<6>fFNWq2>6F%z-r9&<`AJYn1iuGvBVFZ z)hZ@2OwE{@wXR*&YI6-u(n(#c)`+B=nyN1t4O1)Vx=M^2#L&zp&@c204Wnji(}up# zr}NpKo-@2`8Pl^wp`wM^H9ZNhIr683Uu<$W#O7Toz^p@}HKFrKf zE-TWABC5&9gAe74hVR!o;6vc2eC^x(OKIcwt)2WY=ia7dz1n`;)qC+xL8_R`sdLQP zoyKiD!(#X!>s>!!y#n@-wubFnQk#iq?QZiir+|D7M+P6Q6^Iz z??Y3^TLbP^W$?K;4&A6T>O=wG4xo3U74c|f`$pwB;^V*s?C00V#~v*K+`(1g)U@n> zB*$qL=paSyR=rAQ=IW#xHK3jk?86G`TR0f4F(HAo(6~9BCC(0naU427*}D0D#|x6s z7>y&J_afr#%8%Ce@7u23Mz%lRacnO??LQs#%!C^e*3j&e4VzDW1%+`O{u9YI!gF99 zaOea9N~a&9CgYjRK^}h`cLMcp)Lm+aG)LKR&a3MUD&IX TS^_(Zvim*A|DW%1GdsX$` literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crsuppl.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crsuppl.r new file mode 100644 index 0000000000000000000000000000000000000000..0735ab25908889f17b15001c2c69c8fbe0b71ee1 GIT binary patch literal 2263 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 literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/delinv.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/delinv.r new file mode 100644 index 0000000000000000000000000000000000000000..66b6ecbe14579ce3f676a66486201b04dff5a000 GIT binary patch literal 2300 zcmb_e&1(}u6n~q3q?M|*dhnn)6cnK!q_u@8h*^`6tTfq>Y-$fV7@KVp+ipU#DLHA5 z{R2b_iWK!Ap1kQnsNh8p7C{O{J@laB$6b36;(NP0ar4y=@R2vad7m?R^JbFX_v_Oy z%7O|=2jFREUk~&E1O<`-VkWd0ms{va_0|dCa*bVf;eb3@(&t9ps3CH@AtXv+=Dm=J z7u7;BQAlP=3-gI|R!_)zRr9CPs+=z-V)5B}VJWgos~j03E*;wfyX|F8)-buccM>w;=(Zf?X# zR1QS_vG(o2>hd*)H_HOBI%Tfh{l+=<`QxXDfj{>=^H&FNe|U7?dGgi3nzT0Y;P6lG zY%XISHP^MkUHAKLpZldXI+1zZXx@t2>6fwX8tK`Y@2MxJ0M_r2MF`Z}+hsDnwlLoc zasY2D!=91C)3W6ChvOk{Jf#gH5iD~ha$QxQ?}iM}i767q)?IqLIA(Ki;9 zBHo}F4fuh!Nh?zu5)zXRZ;-)wPYlFcZXmuDhEK00(<5>OPQV~utVy_tT7W)e@}s-6 zH>y^5@~a{>+DlqWoy%%!s?k8QZQWHAw~#r#Uvvgx_G@zqku zXlsm9zfpuqt?bd?+zQaj{_BfruOKwrtJR7v|6SbBAuAo#f0ch3OUL1P8$Gx0goiET z(FP|fMpJ#zCnEi;5A_v~XVxD2QQ6xMfFFbQ(lZ?mZ{|6SS<4^WfRRuiNx%igy}%L0GBFd(6m&y(FB*8MCd| zS$8_hObo1FxEd-Mwe}+@F6K&_&SsOEmebjFm8A=6QdbL1&m=XbH@qD!}u<{xVRq8vO;4`vNTh literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/delitem.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/delitem.r new file mode 100644 index 0000000000000000000000000000000000000000..19a151ca57c84f61402bdaf4a728f7024aef0c09 GIT binary patch literal 3105 zcmb_eO>7%g5S~p8xU`T61VX@J1*ihi1}CE679`?0i@j3pHEV~ak|l!VZE7_MmVZzr zBx-TW5s8a$P^pI=dxHZ)IrhMhCB)AO^#UqARXvo##eCoHzT|nmn+l1M=FPmBdGqsT z_Gyp&`2D+I?--r{j>@&vv9zQ`iU#*0)qupTyD>6~?mR4#$;>ZhumDf>y5~=3At7Y5 zQp(w7Gk>PO+VR?*di(tgz0HgDjb^v*w7hMrvEezbPQBXtpxy1%bFEg@Yk%mq>m6UP zkXgud+Z&yF!`p0jy{);HIe+5BtELU!3{2*HKBMKf_JBzLh{!D<`4>|n73kApg)f7D zONP)s4bHAQ+3cy)C+9T}W9&T~8^Xo?;JRjTbz0lRuoE9YgVbeTJlM*WaV?UO0Hm4Tsu#G*{bGU zyHc~AvQsYR%FDTx6$f%_*|i*Z*>WK#p{U@v=jIi&P_0;bJGXMyvJ1sp$u7TBJI5Gy z3Er6r%VMTTL3pL)i5yYlgFwn30#c5*#i#h*e2Uk?r$<3)cPyljg!EBR`k4Xp{4DS> zAU)2=lk$wbB(F#opYYq_$eO$-o?P_xB85p zp2o+q>S$apqf17gXF?l;%+2+WfBGX7IM7AUc{h0?fH=MTm(mL6Z}R-z-vfOgWKusG z^)>&4FppouiMau1d}CO$Qbmsb37ln{6J{&lytZ)g{r#b=YO7 zj6g6CW<)CTIwbpkCbWToZQcsYSP$xFZT_2=P=C_B)YhuPf5u>0YHs^Xxwi%1B3Bto19Yd)?I4sDK`(@Sli7?!Yq9d7J9SC=34c7XP>ZYWh_0FZWd^82|tP literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/delord.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/delord.r new file mode 100644 index 0000000000000000000000000000000000000000..d384de28ad531c03fb8d2c3e284992078b4d7fd2 GIT binary patch literal 3352 zcmb_fL2MgU5S?{GaiBB-Boqk=mL4Kd)5;07m?D84d#M-MUbBAIr&6l~AQ}Kq?1NdqI&}K@Uh~-v0lSpUv7;B}V#x=D+zf ze|Bd6dOh*W&mVr@vqEAzLA&F})08GESy&SZ$r9$x1PsuQ$)Y=fV@3U)MxdS zdcspIze~HUeT^koc-idPw@%NooJbGZY>M4Hif$MicE^lGg4iviwY#(h|;g%GN5*^eG2^|>HRR4Aoj1ryGz{M~ zi=+&{o4MEgt50-s0f2Qu!HNy@B-PU#&o=wKMw7I~vga2azgn*p>LOqkEsOcU5hb?< z&Izfw!hu-G`@ZY=ZdC++y%4xwRn)w|@hd{)%Z~7!+Jajy2cBDmF?i%n1o@yYKFa$} z$*T(oXZcRi4cJ|gJB!0Y#2iY*Ap=9Jf*(`-N#;mBzhfdkp*Y{aXw7erh#zMTAI~U$ zLgABYJ*C$0G)dFkO{Zv{-k}^hRG~Uuq!sG&9uR!vJlx$;^fD`TeIX~&Fnk5U#t8NO z@?WA?9!AfE)EJatU}&$iy2oPyk#iDteamhYsdodX!mQ@I1?Pj{iW&Ot4>8PC8Gj6Cy#KFi89S9UhvlYhei2rb)33KC}8TQ+q53 zw{zES{iy(tb#c4!E-WM^>HUASmTSyn-2U%Bq`uGWsD3c$EB|{O!vtRn+zU`n#UxkB z^O(_*i@YDz)aRq_noQi*AoDbuiK&D}VZt3q^SH}?`wDwiak=B@{H%hWkExK{SiZ^P z7^mEWeB*d+-!h&z%ZeQpo9R~epKU^s{3g%Z&k>~p(nhWTaSx88Cl7jB2C%6Mkl^SX zWBQz&@|TJEO*n_G^f?yy|LAjUg`7rzXzy+z6Y>7s<&v=J;Srfi-MwWVl@}TwXnOozAV3* zC0OS7D2GP0%flotZXfF%OiU#ny1)@&Oy)gbV%&UzV4DnrUkohw05IW`N8?y;0Yigx dsUtj!3>x`j!{tF?bn@lrZSMd2JAq^N=|4$INH+ig literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/delordl.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/delordl.r new file mode 100644 index 0000000000000000000000000000000000000000..67347713ad47074ac26352d4615f96797b935d8b GIT binary patch literal 2071 zcmb_dO=uHQ5S~qIO-0m$l&T=BhhA*UwziPwVt;mN18p{DHxh9V3j3NCW4de->8a)- z^dc1WP^?1nBzUO@@#4Xw7sVo;M2H8ao(1heh%>wUlFeq*A8=rH=6&*+bBA~urw@{>|D_>S$@ej z^MjT+)ZafMsDtRG%q9K0K?JEc)L2|aKQ&O;wuMwG4>pt}X=>>NyLwY_*X zlwiDEFxfm_1!gcnZC}5e*sP**dCQ$PmZygGS-U4xd}#vpvs+pD{KHF9F&NBaJB^I6 zc>2%Y@!#Lwcm+*seyvvV)$cy#&OK#}^BhbZ@`jJ=xTy#)?)c{VH zDHng86bC&k2LIptU?dX#CTC6N7yIIIl4#jsR3Vmu`Y! SoX!vz_f{YOZ?7q@OZFe#0Qp$} literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/delsup.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/delsup.r new file mode 100644 index 0000000000000000000000000000000000000000..b5a070c21f1472cf4977cef78a475c29937ed5e2 GIT binary patch literal 5560 zcmb_gO>7%Q6n<+PNTGjF3K631f=X4IwsL<-D-dzxrEy7Y*LF%OMIvdlZ31m-*-q69 zt+_xXkmxa0FOd*Z#VI#>;E)4{TtJl&QjfWSiiA)vKn_UZ`(|hC*|n3jz>{{~yf^cH z=grLSuKV$aGv8iwd;x5gxy1So=@992NQ)E$)TX`JhDX-N&x<6J2Tml>;5jl?yRfet z9Kx}r45TtyxA*w)VAZcwhbyDwQVOxMD8KV5gln4CciDy&xzbZFH_8QcON;n?|`ks?k`acYSw}UYr;(2BHDE8mcBWw z-)5>}-3-l1h^;;r5~_a{A|>V;`kD?upc`y69L4!U?qtC$u3Q#wNMBUEm3G7pF^_)v z_l}WIJ6}9>;%j%^@7r&6eX*fy{BkOJ{Z8+9ci(@|m>if1Ry?F2OswfIOW<)}9!X9> zz~qhncCZqEI=nDS^qXD3j&0j5vf($9&dIR8l{5vjI^fR?bbcTkd|xi>dO{>9y0WVuB z7W*>!)BtDZbCBxq&859`rl@2C`wWL3!n81z>=QqsF-w&6QySxJQ8ad+!cS}b3@~-A z*YpNp>e{I3O`1lQJ4D=H`L(IjkR-0?XhPymV+?3tq36Ipc9y ztxIXJu_5jCFnwC)dXP5jLC$8`CMdfG+(6IdgZ*}}5`TmSBEWo{PmApNUfF)Ee9S># z8PA(o$%nxGnXHG5WJ=xuP`;4q^-h=0np*URN?sPFsrV{&`5Yx!=|OHxF6adFVRAyZ z*=JseS*PT&en^`-<;X_af|}VbU09_sEH8)YFcp*4$Kz{^F9X6FMLq$;kw12P4jhxu zZaJW45kw65j=~9UJH(N2VtA$|$NUQull~Z)QBX5+jv&}sj4k4sW8i>!)vnxVQAE({ z zS=T;|19vw~wDf{l7z*Q!seHFgYGF94o?_y*j4Z;QeFw2l2`X6%E3(lzut?DEHu!k6 zQP6`nZZfW-o5fUmeYYWU8+??Af(5j^Kjuv$G@oeBIAif1wQKbAEVe;qPS=HYNTIn? z&AOmjBj2fgaN*xN1j&6#c=!muC9IfuPacAtskcQc(O~nJ3=^33!+VLa8D3J>)K9<- z+96%wHt$jUoAkt&BLdvVE&B&^=HCtH$;kIuv6hWLJj^uoAfc{FDtIGS-^{Tw-h&mq zWHZr^qy8d(iG$qC_J{p4^7k#jX5m){57!6;d&!M(Mcx3%=&=XQ#|e47BWytJCz!iH zCr5%pnKm25o~-*vfsLIzTHg%t8g?LM>~L4EZpYQOh}JG$N0NGT7_knl*OC>h(eYo- zi5ONh0BT!+cG8OtE79|l_mo}#t>bZ7?R<_yzd4_2w^P^WkiKgcfIIOlVUsUvUbn`a z-wyD#sUZ5E#;>7vbt+>Ysr%;|^fGEAU$^!{AN-oL_Kr58Ysxib1G7$zo(410=`;tI z<_C4ksxkLwuoi38sj-vS`g$UI+t`6cV~2IRx*gZ@+tirFigX>Bj@0QQ{F$|UGCH^? z35K54a%SHczPY7()a;<4&k1K2h)%q-24Kvt#5a;SA>a*To^E&*t=q&Jb)CTdxmsOw z+^*~6u(maDAk>-|Io=r>2?!Gt-#^SfWq6#i8sF5vv0>)7<8#b$X3#eCxf2uwbijK-TkfL04eT;wc7+10>??mi6@}#O5XN7f-J9{AC z^xxzu8rMF|;@ukMp91f&;=hL0ZPXP#2BQJdxSIL+ckvtLQN7%m?ef)}-@g7!Lq#z^ zyqcZ-ROS5mXw}bE#{7yD{PX_ARlNF}iSGH)Nq6!}%{}M4_?PC_{IUH~8uu%{J5hB< zYwjg~v|4koO!{s<=hnu-fi^ASPr4PYa6dj%`|(l1uhpr`K}(v1DsbbyPA7t_v%8?_ zKH#$MN0I&FID$bIrwt|+!@G%2T@(C;5*?qz*eo7(Vd6XprzjAd2T}(T$Aie!(ZRcc l_W3eMzqs^#Vc?eFM&~ADynI=*U*v+{Zax3k?@nlM!ar2b=i2}P literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/ref_call.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/ref_call.r new file mode 100644 index 0000000000000000000000000000000000000000..e72554097959bb69f72a29b46a17a82950e59b16 GIT binary patch literal 2109 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 literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/writem.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/writem.r new file mode 100644 index 0000000000000000000000000000000000000000..9c5e3b57f2f7c912f5a15d8fe1dfac61a37b7a1c GIT binary patch literal 6313 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 literal 0 HcmV?d00001 diff --git a/SportsApp/Sports/build-output/tests/AppServer/CustomerTest.r b/SportsApp/Sports/build-output/tests/AppServer/CustomerTest.r new file mode 100644 index 0000000000000000000000000000000000000000..099f7b958323a5307e17cc4189b6f71323ba84b8 GIT binary patch literal 12461 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 G Project's root location -// WRKDIR -> Work directory set during installation -// -// Note: System properties, System environment variables, and Eclipse variables are also supported -// -// Use variable as "${}". For example, -// buildDir="${ROOT}/myBuildDir" -// ====================================================================================================== - - -// ====================================================================================================== -// The OpenEdge DLC path. (Ignored in Developer Studio.) -// This property should be set when building the project using OEDF plugin. -// ====================================================================================================== -// dlcHome="" - -// ====================================================================================================== -// The project's build directory path. -// The directory that will contain saved .r files. If this field is blank, .r files are saved in the -// same directory as the source files. -// ====================================================================================================== -buildDir="${ROOT}/build" - -// IDE specific configurations -oeide { - - // ================================================================================================== - // Boolean value to specify if the project uses a shared AVM or a dedicated AVM. - // If true, the values set in this config file will be ignored and the values defined under - // 'Windows->Preferences->SharedAVM' will be honored. - // ================================================================================================== - useSharedAVM="false" - - // ================================================================================================== - // Boolean value to enable/disable oeide events. - // A named event, oeide_event, is published whenever a Developer Studio operation occurs. This - // enables to write procedures that use the ABL SUBSCRIBE statement to capture and respond to - // those events. - // To know more search for 'Event subscription' at https://www.progress.com/documentation. - // ================================================================================================== - oeideEvents="true" - - // ================================================================================================== - // Boolean value to enable/disable the global Visual Designer toolbox. - // ================================================================================================== - useGlobalToolboxVD="false" - - // ================================================================================================== - // Boolean value to enable/disable passing default AVM parameters. - // When enabled, AVM starts with default parameters specified at - // 'Window->Preferences->Progress OpenEdge->Startup'. - // ================================================================================================== - addDefaultParams="true" - - // ================================================================================================== - // Boolean value to enable/disable project-specific runtime console. - // This setting is applicable when 'useSharedAVM' is false and 'tty.enabled' is true. - // ================================================================================================== - hideTTYConsole="false" - - // ================================================================================================== - // Boolean value to enable/disable use of project specific 'strictOptions' under 'compile'. - // Other compiler options listed under 'compile' section are not affected by this setting. - // ================================================================================================== - useProjectCompilerSettings="false" - - // ================================================================================================== - // Boolean value to enable/disable persisting rcode on compiling sources. - // ================================================================================================== - saveRCode="true" - - // ================================================================================================== - // String value to specify the path where cross-reference information is saved in an XML file. - // This corresponds to the COMPILE option, XREF-XML. - // ================================================================================================== - xrefXmlDir="" - - // ================================================================================================== - // String value to specify the path to a procedure that runs prior to compilation. - // ================================================================================================== - preCompileCallbackRoutine="" -} - -// AVM configurations -// **IMPORTANT** Any change in AVM configuration requires restarting AVM. -avm { - // ================================================================================================== - // String value to specify the path to the working directory, from where the AVM runtime starts. - // ================================================================================================== - wrkDir="${ROOT}" - - // AVM options - avmOptions { - // ============================================================================================== - // String value to specify the path to a temporary directory for the AVM runtime (same as -T - // startup parameter option) - // ============================================================================================== - tmpDir="${WRKDIR}" - - // ============================================================================================== - // Boolean value to use '_progres' or 'prowin' executables. 'true' for _progres, 'false' for prowin. - // ============================================================================================== - tty.enabled="true" - - // ============================================================================================== - // String value to pass startup parameters. This will append to default params if - // 'addDefaultParams' is true. - // ============================================================================================== - startupParameters="" - - // ============================================================================================== - // String value to specify the path to assemblies directory (same as -assemblies startup - // parameter option). - // ============================================================================================== - assembliesDir="" - } - - // ================================================================================================== - // Database configuration (Ignored in Developer Studio.) - // Set the database details when building the project using OEDF plugin. - // database { - // - // } - // ================================================================================================== -} - -// Compile configurations -compile { - // ================================================================================================== - // Comma-separated string values specifying the compilable file extensions. - // This property can be uncommented when building the project using OEDF plugin. - // ================================================================================================== - // compilableFileExtensions="p,w,cls,pgen,html,htm" - - compileOptions { - // ============================================================================================== - // Boolean value to set COMPILER:MULTI-COMPILE attribute. - // ============================================================================================== - multiCompile.enabled="false" - - // Strict options - strictOptions { - // ========================================================================================== - // Similar to 'OPTIONS require-full-names' in COMPILE statement. - // Value can be 'ignore/warning/error' - // ========================================================================================== - requireFullNames="Ignore" - - // ========================================================================================== - // Similar to 'OPTIONS require-field-qualifiers' in COMPILE statement. - // Value can be 'ignore/warning/error'. - // ========================================================================================== - requireFieldQualifiers="Ignore" - - // ========================================================================================== - // Similar to 'OPTIONS require-full-keywords' in COMPILE statement. - // Value can be 'ignore/warning/error'. - // ========================================================================================== - requireFullKeywords="Ignore" - - // ========================================================================================== - // Similar to 'OPTIONS require-return-values' in COMPILE statement. - // Value can be 'ignore/warning/error'. - // ========================================================================================== - requireReturnValues="Ignore" - } - - // ============================================================================================== - // String value to specify XCODE option in COMPILE statement. - // ============================================================================================== - xcodeKey="" - - // ============================================================================================== - // Boolean value to set XREF-XML option in COMPILE statement. - // ============================================================================================== - xrefXml.enabled="false" - - // ============================================================================================== - // Boolean value to set STREAM-IO option in COMPILE statement. - // ============================================================================================== - streamIO.enabled="false" - - // LANGUAGES option in COMPILE statement. - languages { - // ========================================================================================== - // A comma-separated list of language segments to include in the compiled r-code. - // ========================================================================================== - list="" - - // ========================================================================================== - // TEXT-SEG-GROW option. Growth-factor as an integer. Supported only when the language list - // is provided. - // ========================================================================================== - textSegGrow="" - } - - // ============================================================================================== - // Boolean value to set MIN-SIZE option in COMPILE statement. - // ============================================================================================== - minSize.enabled="false" - - // ============================================================================================== - // Boolean value to set ATTR-SPACE option in COMPILE statement. - // (Not supported in OEDF) - // ============================================================================================== - attrSpace.enabled="false" - } -} diff --git a/SportsApp/Sports/build.gradle b/SportsApp/Sports/build.gradle deleted file mode 100644 index ef55b21..0000000 --- a/SportsApp/Sports/build.gradle +++ /dev/null @@ -1,142 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * This is a general purpose Gradle build. - * Learn more about Gradle by exploring our samples at https://docs.gradle.org/7.3.3/samples - */ -plugins { - id "progress.openedge.abl" version "2.2.1" -} - - -// Gather required variables -def stageEnv = System.getProperty("STAGE_ENVIRONMENT") -stageEnv = stageEnv != null ? stageEnv : System.getenv("STAGE_ENVIRONMENT") -def STAGE_ENVIRONMENT = stageEnv != null ? stageEnv : "dev" - -println "DLC: ${abl.dlcHome}" -println "Stage Environment: ${STAGE_ENVIRONMENT}" - -group = 'com.progress.openedge' -version = "1.0.0" -description = 'Sports App' - -abl { - if (STAGE_ENVIRONMENT == "dev") { - dbConnection { - dbName="${buildDir}/db/sports2020/sports" - connectionParameters = "-1" - } - } else { - dbConnection { - parameterFile='conf/startup.pf' - } - } -} - -// ABL App tasks -task createSports2020(type: CreateDB){ - dbName = 'sports' - sourceDb = "${dlcHome}/sports2020" - outputDir = "${buildDir}/db/sports2020" -} -if (STAGE_ENVIRONMENT == "dev") { - compileAbl.dependsOn "createSports2020" -} - -tasks.named("compileAbl-root-AppServer"){ - rcodeDir = "${buildDir}/rcode/AppServer" -} - -tasks.named("compileAbl-root-PASOEContent-WEB-INF-openedge"){ - rcodeDir = "${buildDir}/rcode/PASOEContent/WEB-INF/openedge" -} - -tasks.named("compileAbl-root-tests-AppServer"){ - rcodeDir = "${buildDir}/rcode/tests/AppServer" -} - -task testABLApp(type: ABLUnit){ - source("tests/AppServer") - include("**/*Suite.cls") - propath("tests/AppServer", "AppServer") - outputDir = "${buildDir}/test-results/test" - arguments = [haltOnFailure: "true"] -} -if (STAGE_ENVIRONMENT == "dev") { - testABLApp.dependsOn "createSports2020" -} -check.dependsOn "testABLApp" - -task packageWebApp(type: OEWar){ - webAppName = "Sports" - archiveVersion = "" // to ignore version in the archive name, otherwise has to be handled during deployment - verbose = true - projectLocation = project.projectDir - println "projectLocation: ${projectLocation.get()}" - destinationDirectory = project.file "${project.distsDirectory.get()}/webapps" - - // exclude Sources from 'openedge' directory - // (the ANT task used internally does it by default but added here anyway) - exclude "PASOEContent/WEB-INF/openedge/*.(cls|p|i)" - - webInf { - from("${buildDir}/rcode/PASOEContent/WEB-INF/openedge") - into("openedge") - } - - manifest { - attributes "Implementation-Title": "My Sports ABL Web Application" - attributes "Implementation-Version": "1.0.0" - // from ("PASOEContent/META-INF/MANIFEST.MF") - } -} -packageWebApp.dependsOn "compileAbl-root-PASOEContent-WEB-INF-openedge" - -task packageABLApp(type: Oear) { - ablAppName = "Sports" - archiveVersion = "" - destinationDirectory = project.file "${project.distsDirectory.get()}/ablapps" //will create 'Sports.oear' file at this location - tlr { - from 'tlr' - include '**/*.properties' - include '**/*.xml' - } - webapps { - from "${project.distsDirectory.get()}/webapps" - include '**/*.war' - include '**/*.zip' - } - openedge { - from "${buildDir}/rcode/AppServer" - include '**/*.r' - } - conf { - from 'conf' - exclude '**/*.MF' //exclude direct copy of manifest file and append using manifest section - } - manifest { - attributes "Implementation-Title": "My Sports ABL Application" - attributes "Implementation-Version": "1.0.0" - // from ("conf/MANIFEST.MF") - } -} -packageABLApp.dependsOn "compileAbl-root-AppServer" -packageABLApp.dependsOn "packageWebApp" -assemble.dependsOn "packageABLApp" - -task dockerBuild(type:Exec) { - workingDir "${buildDir}/docker" - commandLine 'bash', '-c', "docker build --no-cache -t ${project.name.toLowerCase()}:latest ." - doFirst { - copy { - from "${project.distsDirectory.get()}/ablapps/" - into "${buildDir}/docker/ablapps" - } - copy { - from "Dockerfile" - into "${buildDir}/docker" - } - } -} -dockerBuild.dependsOn build \ No newline at end of file diff --git a/SportsApp/Sports/conf/MANIFEST.MF b/SportsApp/Sports/conf/MANIFEST.MF deleted file mode 100644 index 8651f82..0000000 --- a/SportsApp/Sports/conf/MANIFEST.MF +++ /dev/null @@ -1,3 +0,0 @@ -version: 1.0.0 -name: Sports -deployment-type: ablapp \ No newline at end of file diff --git a/SportsApp/Sports/conf/startup.pf b/SportsApp/Sports/conf/startup.pf deleted file mode 100644 index c380391..0000000 --- a/SportsApp/Sports/conf/startup.pf +++ /dev/null @@ -1 +0,0 @@ --db sports -H oedbmachine -S 7654 \ No newline at end of file diff --git a/SportsApp/Sports/gradle.properties b/SportsApp/Sports/gradle.properties deleted file mode 100644 index a5e00c6..0000000 --- a/SportsApp/Sports/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -# DLC= -# STAGE_ENVIRONMENT= \ No newline at end of file diff --git a/SportsApp/Sports/gradle/wrapper/gradle-wrapper.jar b/SportsApp/Sports/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL diff --git a/SportsApp/Sports/gradle/wrapper/gradle-wrapper.properties b/SportsApp/Sports/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 2e6e589..0000000 --- a/SportsApp/Sports/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/SportsApp/Sports/gradlew b/SportsApp/Sports/gradlew deleted file mode 100644 index c53aefa..0000000 --- a/SportsApp/Sports/gradlew +++ /dev/null @@ -1,234 +0,0 @@ -#!/bin/sh - -# -# Copyright 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions $var, ${var}, ${var:-default}, ${var+SET}, -# ${var#prefix}, ${var%suffix}, and $( cmd ); -# * compound commands having a testable exit status, especially case; -# * various built-in commands including command, set, and ulimit. -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/SportsApp/Sports/gradlew.bat b/SportsApp/Sports/gradlew.bat deleted file mode 100644 index 107acd3..0000000 --- a/SportsApp/Sports/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/SportsApp/Sports/settings.gradle b/SportsApp/Sports/settings.gradle deleted file mode 100644 index ea406c3..0000000 --- a/SportsApp/Sports/settings.gradle +++ /dev/null @@ -1,10 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/7.3.3/userguide/multi_project_builds.html - */ - -rootProject.name = 'Sports' diff --git a/SportsApp/Sports/tests/AppServer/CustomerTest.cls b/SportsApp/Sports/tests/AppServer/CustomerTest.cls deleted file mode 100644 index 3f4a8b6..0000000 --- a/SportsApp/Sports/tests/AppServer/CustomerTest.cls +++ /dev/null @@ -1,162 +0,0 @@ - -/*------------------------------------------------------------------------ - File : CustomerTest - Purpose : - Syntax : - Description : - Author(s) : rahulk - Created : Sat Sep 09 16:43:47 IST 2023 - Notes : - ----------------------------------------------------------------------*/ - -USING Progress.Lang.*. -USING OpenEdge.Core.Assert FROM PROPATH. - -BLOCK-LEVEL ON ERROR UNDO, THROW. - -CLASS CustomerTest: - - /* DEFINE TEMP-TABLE ttOrder */ - /* FIELD Ordernum AS INTEGER LABEL "Order Num" FORMAT "zzzzzzzzz9" INITIAL "0".*/ - /* DEFINE DATASET dsCustomer1 FOR ttOrder. */ - /* - {} */ - -/* {customer.i}*/ - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - @Before. - METHOD PUBLIC VOID setUpBeforeClass( ): - - RETURN. - - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - @Setup. - METHOD PUBLIC VOID setUp( ): - - RETURN. - - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - @TearDown. - METHOD PUBLIC VOID tearDown( ): - - RETURN. - - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - @After. - METHOD PUBLIC VOID tearDownAfterClass( ): - - RETURN. - - END METHOD. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testReadCustomer( ): -/* DEFINE VARIABLE customerVar AS Customer NO-UNDO. */ -/* DEFINE VARIABLE iQuery AS CHARACTER NO-UNDO. */ -/* */ -/* customerVar = NEW Customer(). */ -/* iQuery="WHERE Customer.State = 'UNKNOWN'". */ -/* */ -/* DEFINE DATA-SOURCE srcCustomer FOR Customer. */ -/* /* DEFINE BUFFER OtherCust FOR Customer. */ */ -/* /* */ */ -/* /* DATASET dsCustomer:FILL(). */ */ -/* /* */ */ -/* /* DATASET dsCustomer:HANDLE. */ */ -/* */ -/* BUFFER ttCustomer:ATTACH-DATA-SOURCE(DATA-SOURCE srcCustomer:HANDLE,*/ -/* "Customer.Name,CustName"). */ -/* */ -/* customerVar:ReadCustomer(iQuery, DATASET dsCustomer). */ -/* */ -/* DATASET dsCustomer:FILL(). */ -/* BUFFER ttCustomer:DETACH-DATA-SOURCE(). */ -/* FOR EACH ttCustomer: */ -/* DISPLAY */ -/* ttCustomer.Name */ -/* ttCustomer.State. */ -/* Assert:Equals(ttCustomer.State, "hey"). */ -/* */ -/* END. */ - Assert:IsTrue(TRUE). - RETURN. - - END METHOD. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testCreateCustomer( ): - Assert:IsTrue(TRUE). - RETURN. - - END METHOD. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testcount( ): - DEFINE VARIABLE customerVar AS Customer NO-UNDO. - DEFINE VARIABLE iQuery AS CHARACTER NO-UNDO. - DEFINE VARIABLE oCount AS INTEGER NO-UNDO. - - customerVar = NEW Customer(). - iQuery="WHERE Customer.State = 'UNKNOWN'". - customerVar:count(iQuery, oCount). - Assert:Equals(oCount , 0). - - iQuery="WHERE Customer.State = 'NH'". - customerVar:count(iQuery, oCount). - Assert:NotEqual(oCount , 0). - Assert:IsPositive(oCount). - RETURN. - - END METHOD. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testDeleteCustomer( ): - Assert:IsTrue(TRUE). - RETURN. - - END METHOD. - -END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/tests/AppServer/OrderTest.cls b/SportsApp/Sports/tests/AppServer/OrderTest.cls deleted file mode 100644 index ff81736..0000000 --- a/SportsApp/Sports/tests/AppServer/OrderTest.cls +++ /dev/null @@ -1,43 +0,0 @@ - -/*------------------------------------------------------------------------ - File : OrderTest - Purpose : - Syntax : - Description : - Author(s) : rahulk - Created : Sat Sep 09 19:40:47 IST 2023 - Notes : - ----------------------------------------------------------------------*/ - -USING Progress.Lang.*. -USING OpenEdge.Core.Assert FROM PROPATH. - -BLOCK-LEVEL ON ERROR UNDO, THROW. - -CLASS OrderTest: - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testCreateOrder( ): - Assert:IsTrue(TRUE). - RETURN. - - END METHOD. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testReadOrder( ): - Assert:IsTrue(TRUE). - RETURN. - - END METHOD. - -END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/tests/AppServer/SportsTestSuite.cls b/SportsApp/Sports/tests/AppServer/SportsTestSuite.cls deleted file mode 100644 index 516839c..0000000 --- a/SportsApp/Sports/tests/AppServer/SportsTestSuite.cls +++ /dev/null @@ -1,16 +0,0 @@ - - /*------------------------------------------------------------------------ - File : SportsTestSuite - Syntax : - Author(s) : rahulk - Created : Sat Sep 09 16:41:21 IST 2023 - Notes : - ----------------------------------------------------------------------*/ - -USING Progress.Lang.*. -BLOCK-LEVEL ON ERROR UNDO, THROW. - -@TestSuite(classes= "CustomerTest, OrderTest"). -CLASS SportsTestSuite: - -END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/tests/CustomerTest.cls b/SportsApp/Sports/tests/CustomerTest.cls deleted file mode 100644 index 3f4a8b6..0000000 --- a/SportsApp/Sports/tests/CustomerTest.cls +++ /dev/null @@ -1,162 +0,0 @@ - -/*------------------------------------------------------------------------ - File : CustomerTest - Purpose : - Syntax : - Description : - Author(s) : rahulk - Created : Sat Sep 09 16:43:47 IST 2023 - Notes : - ----------------------------------------------------------------------*/ - -USING Progress.Lang.*. -USING OpenEdge.Core.Assert FROM PROPATH. - -BLOCK-LEVEL ON ERROR UNDO, THROW. - -CLASS CustomerTest: - - /* DEFINE TEMP-TABLE ttOrder */ - /* FIELD Ordernum AS INTEGER LABEL "Order Num" FORMAT "zzzzzzzzz9" INITIAL "0".*/ - /* DEFINE DATASET dsCustomer1 FOR ttOrder. */ - /* - {} */ - -/* {customer.i}*/ - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - @Before. - METHOD PUBLIC VOID setUpBeforeClass( ): - - RETURN. - - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - @Setup. - METHOD PUBLIC VOID setUp( ): - - RETURN. - - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - @TearDown. - METHOD PUBLIC VOID tearDown( ): - - RETURN. - - END METHOD. - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - - @After. - METHOD PUBLIC VOID tearDownAfterClass( ): - - RETURN. - - END METHOD. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testReadCustomer( ): -/* DEFINE VARIABLE customerVar AS Customer NO-UNDO. */ -/* DEFINE VARIABLE iQuery AS CHARACTER NO-UNDO. */ -/* */ -/* customerVar = NEW Customer(). */ -/* iQuery="WHERE Customer.State = 'UNKNOWN'". */ -/* */ -/* DEFINE DATA-SOURCE srcCustomer FOR Customer. */ -/* /* DEFINE BUFFER OtherCust FOR Customer. */ */ -/* /* */ */ -/* /* DATASET dsCustomer:FILL(). */ */ -/* /* */ */ -/* /* DATASET dsCustomer:HANDLE. */ */ -/* */ -/* BUFFER ttCustomer:ATTACH-DATA-SOURCE(DATA-SOURCE srcCustomer:HANDLE,*/ -/* "Customer.Name,CustName"). */ -/* */ -/* customerVar:ReadCustomer(iQuery, DATASET dsCustomer). */ -/* */ -/* DATASET dsCustomer:FILL(). */ -/* BUFFER ttCustomer:DETACH-DATA-SOURCE(). */ -/* FOR EACH ttCustomer: */ -/* DISPLAY */ -/* ttCustomer.Name */ -/* ttCustomer.State. */ -/* Assert:Equals(ttCustomer.State, "hey"). */ -/* */ -/* END. */ - Assert:IsTrue(TRUE). - RETURN. - - END METHOD. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testCreateCustomer( ): - Assert:IsTrue(TRUE). - RETURN. - - END METHOD. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testcount( ): - DEFINE VARIABLE customerVar AS Customer NO-UNDO. - DEFINE VARIABLE iQuery AS CHARACTER NO-UNDO. - DEFINE VARIABLE oCount AS INTEGER NO-UNDO. - - customerVar = NEW Customer(). - iQuery="WHERE Customer.State = 'UNKNOWN'". - customerVar:count(iQuery, oCount). - Assert:Equals(oCount , 0). - - iQuery="WHERE Customer.State = 'NH'". - customerVar:count(iQuery, oCount). - Assert:NotEqual(oCount , 0). - Assert:IsPositive(oCount). - RETURN. - - END METHOD. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testDeleteCustomer( ): - Assert:IsTrue(TRUE). - RETURN. - - END METHOD. - -END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/tests/OrderTest.cls b/SportsApp/Sports/tests/OrderTest.cls deleted file mode 100644 index ff81736..0000000 --- a/SportsApp/Sports/tests/OrderTest.cls +++ /dev/null @@ -1,43 +0,0 @@ - -/*------------------------------------------------------------------------ - File : OrderTest - Purpose : - Syntax : - Description : - Author(s) : rahulk - Created : Sat Sep 09 19:40:47 IST 2023 - Notes : - ----------------------------------------------------------------------*/ - -USING Progress.Lang.*. -USING OpenEdge.Core.Assert FROM PROPATH. - -BLOCK-LEVEL ON ERROR UNDO, THROW. - -CLASS OrderTest: - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testCreateOrder( ): - Assert:IsTrue(TRUE). - RETURN. - - END METHOD. - - - /*------------------------------------------------------------------------------ - Purpose: - Notes: - ------------------------------------------------------------------------------*/ - @Test. - METHOD PUBLIC VOID testReadOrder( ): - Assert:IsTrue(TRUE). - RETURN. - - END METHOD. - -END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/tests/SportsTestSuite.cls b/SportsApp/Sports/tests/SportsTestSuite.cls deleted file mode 100644 index 516839c..0000000 --- a/SportsApp/Sports/tests/SportsTestSuite.cls +++ /dev/null @@ -1,16 +0,0 @@ - - /*------------------------------------------------------------------------ - File : SportsTestSuite - Syntax : - Author(s) : rahulk - Created : Sat Sep 09 16:41:21 IST 2023 - Notes : - ----------------------------------------------------------------------*/ - -USING Progress.Lang.*. -BLOCK-LEVEL ON ERROR UNDO, THROW. - -@TestSuite(classes= "CustomerTest, OrderTest"). -CLASS SportsTestSuite: - -END CLASS. \ No newline at end of file diff --git a/SportsApp/Sports/tlr/merge.properties b/SportsApp/Sports/tlr/merge.properties deleted file mode 100644 index 3563f5c..0000000 --- a/SportsApp/Sports/tlr/merge.properties +++ /dev/null @@ -1,52 +0,0 @@ -# ABL App props -[Sports] - webApps=Sports - -[AppServer.Agent.Sports] - numInitialSessions=2 - PROPATH=${CATALINA_BASE}/webapps/Sports/WEB-INF/openedge,${CATALINA_BASE}/ablapps/Sports/openedge,${CATALINA_BASE}/openedge,${DLC}/tty,${DLC}/tty/netlib/OpenEdge.Net.apl - uuid=http://${psc.as.host.name}:/Sports - -[AppServer.SessMgr.Sports] - agentStartupParam=-T "${CATALINA_BASE}/temp" -pf ${CATALINA_BASE}/ablapps/Sports/conf/startup.pf - agentLogEntryTypes=ASPlumbing,DB.Connects - agentLogFile=${catalina.base}/logs/SportsApp.agent.{yyyy-mm-dd}.log - -# Web App props -[Sports.Sports] - allowRuntimeUpdates=1 - collectMetrics=1 - statusEnabled=1 - -# See $CATALINA_HOME/conf/openedge.properties.README for details on the properties below. -# DO NOT MODIFY any ${} tags -# Transport properties for the APSV protocol -[Sports.Sports.APSV] - adapterEnabled=1 - enableRequestChunking=1 - oepingEnabled=0 - oepingProcedure= - serviceFaultLevel=1 - useHTTPSessions=1 - -# Transport properties for the SOAP protocol -[Sports.Sports.SOAP] - adapterEnabled=1 - adminEnabled=1 - adminSoapAction=urn:services-progress-com:wsa-admin:01 - debugClients= - wsaUrl=http://${psc.as.host.name}:${psc.as.http.port}/Sports/soap - wsdlEnabled=1 - -# Transport properties for the REST protocol -[Sports.Sports.REST] - adapterEnabled=1 - -# Transport properties for the WEB protocol -[Sports.Sports.WEB] - adapterEnabled=1 - defaultCookieDomain= - defaultCookiePath= - defaultHandler=OpenEdge.Web.CompatibilityHandler - srvrDebug=0 - wsRoot=/Sports/static/webspeed \ No newline at end of file diff --git a/SportsApp/Sports/tlr/openedge.properties.merge b/SportsApp/Sports/tlr/openedge.properties.merge deleted file mode 100644 index 6cfdcc0..0000000 --- a/SportsApp/Sports/tlr/openedge.properties.merge +++ /dev/null @@ -1,2 +0,0 @@ -[AppServer.SessMgr.${oepas-app}] - agentStartupParam=-T "${CATALINA_BASE}/temp" -pf ${CATALINA_BASE}/ablapps/${oepas-app}/conf/startup.pf diff --git a/SportsApp/Sports/velocity.log b/SportsApp/Sports/velocity.log new file mode 100644 index 0000000..e462b39 --- /dev/null +++ b/SportsApp/Sports/velocity.log @@ -0,0 +1,420 @@ +2023-09-13 16:43:55,589 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 16:43:55,589 - Initializing Velocity, Calling init()... +2023-09-13 16:43:55,589 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 16:43:55,589 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 16:43:55,589 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 16:43:55,589 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 16:43:55,589 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 16:43:55,589 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 16:43:55,596 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 16:43:55,597 - Do unicode file recognition: false +2023-09-13 16:43:55,597 - FileResourceLoader : adding path '.' +2023-09-13 16:43:55,615 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 16:43:55,617 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 16:43:55,618 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 16:43:55,620 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 16:43:55,621 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 16:43:55,621 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 16:43:55,622 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 16:43:55,624 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 16:43:55,626 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 16:43:55,627 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 16:43:55,672 - Created '20' parsers. +2023-09-13 16:43:55,680 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 16:43:55,680 - Velocimacro : Default library not found. +2023-09-13 16:43:55,680 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 16:43:55,680 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 16:43:55,680 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 16:43:55,680 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 17:36:45,204 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 17:36:45,205 - Initializing Velocity, Calling init()... +2023-09-13 17:36:45,205 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 17:36:45,205 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 17:36:45,205 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 17:36:45,205 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 17:36:45,205 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:36:45,205 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:36:45,211 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 17:36:45,213 - Do unicode file recognition: false +2023-09-13 17:36:45,213 - FileResourceLoader : adding path '.' +2023-09-13 17:36:45,227 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 17:36:45,228 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 17:36:45,230 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 17:36:45,231 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 17:36:45,232 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 17:36:45,232 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 17:36:45,234 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 17:36:45,235 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 17:36:45,237 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 17:36:45,238 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 17:36:45,274 - Created '20' parsers. +2023-09-13 17:36:45,282 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 17:36:45,282 - Velocimacro : Default library not found. +2023-09-13 17:36:45,282 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 17:36:45,282 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 17:36:45,282 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 17:36:45,282 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 17:37:19,357 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 17:37:19,357 - Initializing Velocity, Calling init()... +2023-09-13 17:37:19,357 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 17:37:19,357 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 17:37:19,357 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 17:37:19,357 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 17:37:19,357 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:37:19,357 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:37:19,363 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 17:37:19,364 - Do unicode file recognition: false +2023-09-13 17:37:19,364 - FileResourceLoader : adding path '.' +2023-09-13 17:37:19,378 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 17:37:19,379 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 17:37:19,380 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 17:37:19,382 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 17:37:19,383 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 17:37:19,383 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 17:37:19,385 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 17:37:19,387 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 17:37:19,389 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 17:37:19,390 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 17:37:19,423 - Created '20' parsers. +2023-09-13 17:37:19,429 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 17:37:19,429 - Velocimacro : Default library not found. +2023-09-13 17:37:19,429 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 17:37:19,429 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 17:37:19,429 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 17:37:19,429 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 17:38:57,237 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 17:38:57,238 - Initializing Velocity, Calling init()... +2023-09-13 17:38:57,238 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 17:38:57,238 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 17:38:57,238 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 17:38:57,238 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 17:38:57,238 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:38:57,238 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:38:57,246 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 17:38:57,247 - Do unicode file recognition: false +2023-09-13 17:38:57,248 - FileResourceLoader : adding path '.' +2023-09-13 17:38:57,268 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 17:38:57,270 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 17:38:57,273 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 17:38:57,275 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 17:38:57,276 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 17:38:57,277 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 17:38:57,278 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 17:38:57,280 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 17:38:57,282 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 17:38:57,284 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 17:38:57,331 - Created '20' parsers. +2023-09-13 17:38:57,340 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 17:38:57,340 - Velocimacro : Default library not found. +2023-09-13 17:38:57,340 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 17:38:57,340 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 17:38:57,340 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 17:38:57,340 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 17:43:28,750 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 17:43:28,751 - Initializing Velocity, Calling init()... +2023-09-13 17:43:28,751 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 17:43:28,751 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 17:43:28,751 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 17:43:28,751 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 17:43:28,751 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:43:28,751 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:43:28,757 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 17:43:28,758 - Do unicode file recognition: false +2023-09-13 17:43:28,758 - FileResourceLoader : adding path '.' +2023-09-13 17:43:28,770 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 17:43:28,771 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 17:43:28,773 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 17:43:28,774 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 17:43:28,775 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 17:43:28,776 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 17:43:28,778 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 17:43:28,779 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 17:43:28,781 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 17:43:28,782 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 17:43:28,816 - Created '20' parsers. +2023-09-13 17:43:28,821 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 17:43:28,821 - Velocimacro : Default library not found. +2023-09-13 17:43:28,821 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 17:43:28,821 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 17:43:28,821 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 17:43:28,821 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 17:46:43,590 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 17:46:43,590 - Initializing Velocity, Calling init()... +2023-09-13 17:46:43,590 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 17:46:43,590 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 17:46:43,590 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 17:46:43,590 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 17:46:43,590 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:46:43,590 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:46:43,597 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 17:46:43,599 - Do unicode file recognition: false +2023-09-13 17:46:43,599 - FileResourceLoader : adding path '.' +2023-09-13 17:46:43,614 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 17:46:43,615 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 17:46:43,618 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 17:46:43,619 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 17:46:43,620 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 17:46:43,620 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 17:46:43,622 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 17:46:43,624 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 17:46:43,626 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 17:46:43,627 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 17:46:43,668 - Created '20' parsers. +2023-09-13 17:46:43,675 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 17:46:43,676 - Velocimacro : Default library not found. +2023-09-13 17:46:43,676 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 17:46:43,676 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 17:46:43,676 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 17:46:43,676 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 17:48:37,828 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 17:48:37,829 - Initializing Velocity, Calling init()... +2023-09-13 17:48:37,829 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 17:48:37,829 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 17:48:37,829 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 17:48:37,829 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 17:48:37,829 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:48:37,829 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:48:37,836 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 17:48:37,837 - Do unicode file recognition: false +2023-09-13 17:48:37,837 - FileResourceLoader : adding path '.' +2023-09-13 17:48:37,857 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 17:48:37,859 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 17:48:37,862 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 17:48:37,863 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 17:48:37,864 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 17:48:37,865 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 17:48:37,868 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 17:48:37,869 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 17:48:37,872 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 17:48:37,874 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 17:48:37,925 - Created '20' parsers. +2023-09-13 17:48:37,935 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 17:48:37,936 - Velocimacro : Default library not found. +2023-09-13 17:48:37,936 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 17:48:37,936 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 17:48:37,936 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 17:48:37,936 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 17:52:54,084 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 17:52:54,085 - Initializing Velocity, Calling init()... +2023-09-13 17:52:54,085 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 17:52:54,085 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 17:52:54,085 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 17:52:54,085 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 17:52:54,085 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:52:54,085 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 17:52:54,091 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 17:52:54,092 - Do unicode file recognition: false +2023-09-13 17:52:54,092 - FileResourceLoader : adding path '.' +2023-09-13 17:52:54,111 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 17:52:54,113 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 17:52:54,114 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 17:52:54,116 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 17:52:54,117 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 17:52:54,117 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 17:52:54,119 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 17:52:54,120 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 17:52:54,122 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 17:52:54,124 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 17:52:54,165 - Created '20' parsers. +2023-09-13 17:52:54,174 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 17:52:54,175 - Velocimacro : Default library not found. +2023-09-13 17:52:54,175 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 17:52:54,175 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 17:52:54,175 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 17:52:54,175 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 18:02:31,615 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 18:02:31,615 - Initializing Velocity, Calling init()... +2023-09-13 18:02:31,615 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 18:02:31,615 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 18:02:31,615 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 18:02:31,615 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 18:02:31,615 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 18:02:31,615 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 18:02:31,625 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 18:02:31,627 - Do unicode file recognition: false +2023-09-13 18:02:31,627 - FileResourceLoader : adding path '.' +2023-09-13 18:02:31,646 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 18:02:31,650 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 18:02:31,652 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 18:02:31,653 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 18:02:31,655 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 18:02:31,655 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 18:02:31,657 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 18:02:31,659 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 18:02:31,661 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 18:02:31,663 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 18:02:31,710 - Created '20' parsers. +2023-09-13 18:02:31,720 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 18:02:31,720 - Velocimacro : Default library not found. +2023-09-13 18:02:31,720 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 18:02:31,720 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 18:02:31,720 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 18:02:31,720 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 18:18:59,307 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 18:18:59,308 - Initializing Velocity, Calling init()... +2023-09-13 18:18:59,308 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 18:18:59,308 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 18:18:59,308 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 18:18:59,308 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 18:18:59,308 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 18:18:59,308 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 18:18:59,320 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 18:18:59,323 - Do unicode file recognition: false +2023-09-13 18:18:59,323 - FileResourceLoader : adding path '.' +2023-09-13 18:18:59,353 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 18:18:59,355 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 18:18:59,357 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 18:18:59,359 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 18:18:59,361 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 18:18:59,361 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 18:18:59,365 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 18:18:59,369 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 18:18:59,372 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 18:18:59,374 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 18:18:59,491 - Created '20' parsers. +2023-09-13 18:18:59,509 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 18:18:59,509 - Velocimacro : Default library not found. +2023-09-13 18:18:59,509 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 18:18:59,509 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 18:18:59,509 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 18:18:59,509 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 18:47:53,538 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 18:47:53,538 - Initializing Velocity, Calling init()... +2023-09-13 18:47:53,538 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 18:47:53,538 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 18:47:53,538 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 18:47:53,538 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 18:47:53,538 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 18:47:53,538 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 18:47:53,547 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 18:47:53,548 - Do unicode file recognition: false +2023-09-13 18:47:53,548 - FileResourceLoader : adding path '.' +2023-09-13 18:47:53,562 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 18:47:53,566 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 18:47:53,568 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 18:47:53,571 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 18:47:53,572 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 18:47:53,573 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 18:47:53,575 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 18:47:53,578 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 18:47:53,580 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 18:47:53,582 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 18:47:53,636 - Created '20' parsers. +2023-09-13 18:47:53,645 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 18:47:53,646 - Velocimacro : Default library not found. +2023-09-13 18:47:53,646 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 18:47:53,646 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 18:47:53,646 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 18:47:53,646 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 18:48:49,042 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 18:48:49,043 - Initializing Velocity, Calling init()... +2023-09-13 18:48:49,043 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 18:48:49,043 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 18:48:49,043 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 18:48:49,043 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 18:48:49,043 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 18:48:49,043 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 18:48:49,048 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 18:48:49,050 - Do unicode file recognition: false +2023-09-13 18:48:49,050 - FileResourceLoader : adding path '.' +2023-09-13 18:48:49,064 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 18:48:49,066 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 18:48:49,067 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 18:48:49,068 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 18:48:49,069 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 18:48:49,071 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 18:48:49,072 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 18:48:49,075 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 18:48:49,077 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 18:48:49,078 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 18:48:49,117 - Created '20' parsers. +2023-09-13 18:48:49,127 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 18:48:49,127 - Velocimacro : Default library not found. +2023-09-13 18:48:49,127 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 18:48:49,127 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 18:48:49,127 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 18:48:49,127 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 19:42:08,275 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 19:42:08,275 - Initializing Velocity, Calling init()... +2023-09-13 19:42:08,275 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 19:42:08,275 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 19:42:08,275 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 19:42:08,275 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 19:42:08,275 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 19:42:08,276 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 19:42:08,283 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 19:42:08,285 - Do unicode file recognition: false +2023-09-13 19:42:08,285 - FileResourceLoader : adding path '.' +2023-09-13 19:42:08,306 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 19:42:08,308 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 19:42:08,310 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 19:42:08,311 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 19:42:08,311 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 19:42:08,313 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 19:42:08,315 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 19:42:08,316 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 19:42:08,317 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 19:42:08,320 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 19:42:08,376 - Created '20' parsers. +2023-09-13 19:42:08,383 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 19:42:08,383 - Velocimacro : Default library not found. +2023-09-13 19:42:08,383 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 19:42:08,383 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 19:42:08,383 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 19:42:08,383 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 19:43:40,447 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 19:43:40,448 - Initializing Velocity, Calling init()... +2023-09-13 19:43:40,448 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 19:43:40,448 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 19:43:40,448 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 19:43:40,448 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 19:43:40,448 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 19:43:40,448 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 19:43:40,452 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 19:43:40,453 - Do unicode file recognition: false +2023-09-13 19:43:40,453 - FileResourceLoader : adding path '.' +2023-09-13 19:43:40,464 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 19:43:40,465 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 19:43:40,466 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 19:43:40,466 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 19:43:40,467 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 19:43:40,467 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 19:43:40,468 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 19:43:40,469 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 19:43:40,470 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 19:43:40,471 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 19:43:40,498 - Created '20' parsers. +2023-09-13 19:43:40,503 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 19:43:40,504 - Velocimacro : Default library not found. +2023-09-13 19:43:40,504 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 19:43:40,504 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 19:43:40,504 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 19:43:40,504 - Velocimacro : autoload off : VM system will not automatically reload global library macros +2023-09-13 19:44:18,104 - Log4JLogChute initialized using file 'velocity.log' +2023-09-13 19:44:18,104 - Initializing Velocity, Calling init()... +2023-09-13 19:44:18,104 - Starting Apache Velocity v1.7 (compiled: 2010-11-19 12:14:37) +2023-09-13 19:44:18,104 - Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties +2023-09-13 19:44:18,104 - Trying to use logger class org.apache.velocity.runtime.log.AvalonLogChute +2023-09-13 19:44:18,104 - Target log system for org.apache.velocity.runtime.log.AvalonLogChute is not available (java.lang.NoClassDefFoundError: org/apache/log/format/Formatter). Falling back to next log system... +2023-09-13 19:44:18,104 - Trying to use logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 19:44:18,104 - Using logger class org.apache.velocity.runtime.log.Log4JLogChute +2023-09-13 19:44:18,111 - ResourceLoader instantiated: org.apache.velocity.runtime.resource.loader.FileResourceLoader +2023-09-13 19:44:18,113 - Do unicode file recognition: false +2023-09-13 19:44:18,113 - FileResourceLoader : adding path '.' +2023-09-13 19:44:18,127 - ResourceCache: initialized (class org.apache.velocity.runtime.resource.ResourceCacheImpl) with class java.util.Collections$SynchronizedMap cache map. +2023-09-13 19:44:18,128 - Loaded System Directive: org.apache.velocity.runtime.directive.Stop +2023-09-13 19:44:18,129 - Loaded System Directive: org.apache.velocity.runtime.directive.Define +2023-09-13 19:44:18,130 - Loaded System Directive: org.apache.velocity.runtime.directive.Break +2023-09-13 19:44:18,131 - Loaded System Directive: org.apache.velocity.runtime.directive.Evaluate +2023-09-13 19:44:18,132 - Loaded System Directive: org.apache.velocity.runtime.directive.Literal +2023-09-13 19:44:18,134 - Loaded System Directive: org.apache.velocity.runtime.directive.Macro +2023-09-13 19:44:18,135 - Loaded System Directive: org.apache.velocity.runtime.directive.Parse +2023-09-13 19:44:18,137 - Loaded System Directive: org.apache.velocity.runtime.directive.Include +2023-09-13 19:44:18,138 - Loaded System Directive: org.apache.velocity.runtime.directive.Foreach +2023-09-13 19:44:18,176 - Created '20' parsers. +2023-09-13 19:44:18,184 - Velocimacro : "velocimacro.library" is not set. Trying default library: VM_global_library.vm +2023-09-13 19:44:18,184 - Velocimacro : Default library not found. +2023-09-13 19:44:18,184 - Velocimacro : allowInline = true : VMs can be defined inline in templates +2023-09-13 19:44:18,184 - Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions +2023-09-13 19:44:18,184 - Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed. +2023-09-13 19:44:18,184 - Velocimacro : autoload off : VM system will not automatically reload global library macros diff --git a/SportsApp/build.gradle b/SportsApp/build.gradle deleted file mode 100644 index 5811738..0000000 --- a/SportsApp/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * This is a general purpose Gradle build. - * Learn more about Gradle by exploring our samples at https://docs.gradle.org/7.3.3/samples - */ -plugins { - id("base") -} - -task packageDeploy(type: Tar) { - archiveFileName = "sportsApp.tar.gz" - destinationDirectory = project.distsDirectory - compression = Compression.GZIP - from ("deploy"){ - include "scripts/**" - include "*.sh" - include "*.yml" - include "Dockerfile" - exclude "license" - into "deploy" - } - from ("webui"){ - into "webui" - } - from ("Sports/build/distributions/Sports.oear"){ - into "deploy/ablapps" - } -} -packageDeploy.dependsOn ":Sports:build" -build.dependsOn packageDeploy \ No newline at end of file diff --git a/SportsApp/deploy/.gitignore b/SportsApp/deploy/.gitignore deleted file mode 100644 index 5629c95..0000000 --- a/SportsApp/deploy/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -output -webui \ No newline at end of file diff --git a/SportsApp/deploy/Dockerfile b/SportsApp/deploy/Dockerfile deleted file mode 100644 index c2410da..0000000 --- a/SportsApp/deploy/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM alpine:3.8 - -# Root location -ARG ROOT_FOLDER=/deploy-staging - -ARG MANIFEST_VERSION=1.0 - -# Copy archive file -COPY ./ablapps/ ${ROOT_FOLDER}/artifacts/ablapps - -# Create "META-INF/MANIFEST.MF" file -RUN mkdir ${ROOT_FOLDER}/META-INF -RUN echo "Manifest-Version: ${MANIFEST_VERSION}" > ${ROOT_FOLDER}/META-INF/MANIFEST.MF -RUN echo "Date-Timestamp: `date +'%Y-%m-%dT%H:%M:%S%z'`" >> ${ROOT_FOLDER}/META-INF/MANIFEST.MF - -# Set working directory -WORKDIR ${ROOT_FOLDER} diff --git a/SportsApp/deploy/ablapps/.gitignore b/SportsApp/deploy/ablapps/.gitignore deleted file mode 100644 index 86d0cb2..0000000 --- a/SportsApp/deploy/ablapps/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file diff --git a/SportsApp/deploy/deploy.sh b/SportsApp/deploy/deploy.sh deleted file mode 100644 index 877c54f..0000000 --- a/SportsApp/deploy/deploy.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -# create the app docker image -docker build --no-cache -t sports:latest . - -# deploy -PAS_INSTANCE_NAME=oepas1 -docker-compose -p ${PAS_INSTANCE_NAME} up -d -echo "PASOE instance named '${PAS_INSTANCE_NAME}_dc' will be available at 'https://localhost:8811'" \ No newline at end of file diff --git a/SportsApp/deploy/docker-compose.yml b/SportsApp/deploy/docker-compose.yml deleted file mode 100644 index 4e0375f..0000000 --- a/SportsApp/deploy/docker-compose.yml +++ /dev/null @@ -1,56 +0,0 @@ -version: "3.6" -services: - oedbmachine: - image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-db:12.8.0 - ports: - - "7654:7654" - - "7664-7684:7664-7684" - environment: - - DB_BROKER_PORT=7654 - - DB_MINPORT=7664 - - DB_MAXPORT=7684 - web: - image: nginx - ports: - - "8080:80" - volumes: - - ./../webui:/usr/share/nginx/html:ro - jdk: - image: eclipse-temurin:17.0.3_7-jdk-centos7 - volumes: - - jdk_dc:/opt/java/openjdk - ablapp: - image: sports:latest - volumes: - - app_dc:/deploy-staging/artifacts - pasoeinstance: - image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0 - depends_on: - - jdk - - ablapp - environment: - - FLUENTBIT_LOGGING=true - - APP_NAME=sports - - INSTANCE_NAME=oepas1 - ports: - - "8811:8811" - container_name: "oepas1_pasoeinstance_dc" - command: ["/bin/sh", "-c", "sh /deploy/scripts/startServer.sh"] - volumes: - - type: volume - source: jdk_dc - target: /usr/java - volume: - nocopy: true - - type: volume - source: app_dc - target: /deploy/artifacts - volume: - nocopy: true - - ./license/progress.cfg:/psc/dlc/progress.cfg - # - ./conf/runtime.properties:/deploy/scripts/config/runtime.properties - # - ./conf/logging:/fluentbit-tlr - - ./scripts/startServer.sh:/deploy/scripts/startServer.sh -volumes: - jdk_dc: - app_dc: diff --git a/SportsApp/deploy/license/.gitignore b/SportsApp/deploy/license/.gitignore deleted file mode 100644 index 86d0cb2..0000000 --- a/SportsApp/deploy/license/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file diff --git a/SportsApp/deploy/license/progress.cfg b/SportsApp/deploy/license/progress.cfg new file mode 100644 index 0000000000000000000000000000000000000000..77ba3e13e8dcac7f30ab8185783731dc7a1b1510 GIT binary patch literal 5408 zcmcJT&2HjY5QSNfCT}nczrbY1Km4ROmVdd}#`g8KV_)x>jweDxLqtfAdY4D)H_V(e z0ksX0MuKJqg2i!h>eN^DTe{smZky-lf#PhwJT2DW2Hn@&;Puy=@2}tA2C8;Dv7(e& z%KoKlx-lG$CwJ|~h)<=IqAayqW92OLlj+RY^m;hV-JzyNEUT^i!c$9M1l4_+#BA{P zIvCsx2Cr{7H#Y-$=L$CFap8t@$DF_7o41=+{NnBAAW?CfuZq~cJS;a#;&o)^FO3qs ztQ1vM)bS^M@@c3l+Mk;K%i#a&4@E95ITd+otBmQPC9RpQ>A|RSqM@qCNgh~5;W-s^ z;)+*^mv;5nrY%gJ$8xS4j%oa;Kk<}uv;F6ByLow1GS{Aqj&}NR9sQh7%RI`Y&tzl` zt(mXt^{C8)QdJGof6u+J@`cZMnfCQd)6Q6t#`-)k969OfPn+%YX1!Q_ApZ*brK=T| z>Xah1MLg0YtFVHA{8PG738zg(;GzDZh@F^aIXlsR=L@E%;n=}%yYJk;MI{Z%H|yyt)OjtLX>m!w~I z`~&}MisL$px860|r|t5&=|8~b{;5%at~xRgAuiQJui!yU{)eN;{|xm<|2lar;QxF& z@A<#Fb2C$>Vd3iN-;RC}&r`*ESUf>}U-g}JVE_a_}{Z;fojxwh5D<~ zm~!+l_rpA;{uRvY*S~g!Ip(461km60|5LN}9v&MdYo1n0x_Ws&d5SCOw@2I(jzIl6 z8yV2=L4RBSU`YPwHT1Xj_bc*$Pk(K~KV$T-tM&bZ{2}-Jz1e=~pv(1>e=Ic|=1C^} zkqo_(`q!vORgV6Zt$&Jw>Mzj0;>167P5FcTbW#7V|DnHF?Y0X{sP%5USv@Pav6pxL zgUj`YSJWiAxKBc(e7F4wPFBT4z0r|r z&T84LcZ&7-GvPw_b-8}zKS%zw{cBVI7S;^;1O3Q<>R*BSxB8L))W7}wnfMtSr_{e^ z=I{JvwOjtaYt{=qK|2q+PjI<@JFg`2Um*XbU+JMg^l1K4{s)*}G0VX}=-)DSY<7Pp zKgs{dpJE=uKb`vXmuCCzkIWNAly_}&{1YzMuVu)8K2t@MaUb>H&oB7D<%h%k@-e@N zA5Q02T{G%m%Kwx4AM$hlxVu$Em)!P`a25T?f2u$9yE^G_^K-9%XryiaQ~gPQ#ZLJX z{*i6|^z|S46K@_Y(FW=D`IDEVzZ7Yv62IArH?zTSL&{I$H}Es^KP*!bfZtB@AN*`W ze=(=|+wsqPFTmo7l*oqy9jxke@Aow)!2BI_Te1{c^JRKkkOpe3N^pv&toFXXoh{LjG8x*K4B>C_+oKk(aekG#5mH5=(%rTXUxNEw1Z!T-cR zZT`dwufhM|AL2J&PW>J~nC3qJ$HYHf{i!AJ2+?wPvbG$b;_~`yxh6?}PW#ss{Mqt@ zf&GQ}UnTwoKcIhU|APLw#}D;{fq!^BMt=7A?dX5HKm4rx2R{@42=p%pKSz80bbrA9 zPW$7Ye;n|e4}NC-{Y!#>Or7>ex}S9YbMSxeo}0b&CtP0tNF)XV2-~79aX*Q`Kf(U~ zfc#g78u*9yU$xEuqDoKn6aUC`U;pm>GNuuoewx+h=?f94`1ZVOz8t-C9sQT??-lqN z{E7Vw{MM~M{IlQRKkD9V)Ia3855TTjq8 z;D6k&;Qy|FydU<)^8@U1|AbjpSQ7rn{fZ%fY{#ej58WRE?C)`MPxBY|H(%J;-%s>c d@DJ@TMNIkGxj!I$e%QZg4NkU;&;QRq{{ce+GT8tC literal 0 HcmV?d00001 diff --git a/SportsApp/deploy/scripts/startServer.sh b/SportsApp/deploy/scripts/startServer.sh deleted file mode 100644 index 000bfcb..0000000 --- a/SportsApp/deploy/scripts/startServer.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash - -# Start-up script for PAS for OpenEdge (PASOE) Docker container. -# Creates, deploys and starts the PASOE server. -# Starts the FluentBit process, if 'FLUENTBIT_LOGGING=true'. - -# ------------------------------------------------------------------------------------------------ -# Environment variables provided with the Docker image: -# DLC Path to PAS for OpenEdge installation (/psc/dlc) -# -# WRKDIR Path to the Working directory (/psc/wrk) -# -# JAVA_HOME Path to Java Development Kit (JDK) installation (/usr/java) -# -# Environment variables to be provided: -# INSTANCE_NAME PAS for OpenEdge Instance name -# -# APP_NAME (Optional) Aplication name, will be used if FluentBit logging is be enabled. -# This will get logged as a new field in each log entry -# -# FLUENTBIT_LOGGING (Optional) If set to the string 'true', FluentBit logging will be enabled -# Example: FLUENTBIT_LOGGING="true" -# ------------------------------------------------------------------------------------------------ - -# ------------------------------------------------------------------------------------------------ -# Artifacts to be provided: -# License Should be provided at DLC location -# (progress.cfg) -# -# Java Development Should be installed at JAVA_HOME location -# Kit (JDK) -# -# .zip Should be provided at '/deploy/artifacts' -# This is the unit of deployment. Refer to the documentation for more details -# -# runtime.properties (Optional) Should be provided at '/deploy/scripts/config' location. -# This can be used to provide runtime configurations. -# Example: .DB.CONNECTION.PARAMS=-db ... -# ------------------------------------------------------------------------------------------------ - -set -e - -## Validations -# Validate if DLC is set -if [ -z ${DLC} ]; then echo "DLC is not set"; exit 1; fi - -# Validate if WRKDIR is set -if [ -z ${WRKDIR} ]; then echo "WRKDIR is not set"; exit 1; fi - -# Validate if license is provided -if [ ! -s ${DLC}/progress.cfg ]; then echo "License is not provided at ${DLC}"; exit 1; fi - -# Validate if JAVA is present -if [ -z ${JAVA_HOME} ]; then echo "JAVA_HOME is not set"; exit 1; -elif [ ! -x "${JAVA_HOME}/bin/java" ]; then echo "JAVA not found at ${JAVA_HOME}"; exit 1; fi - -# Validate if INSTANCE_NAME is set -if [ -z ${INSTANCE_NAME} ]; then echo "INSTANCE_NAME is not set"; exit 1; fi - -# Validate if ABL App archive is provided -if [ ! -s "/deploy/artifacts/ablapps/Sports.oear" ]; then echo "'Sports.oear' is not provided at '/deploy/artifacts/ablapps'"; exit 1; fi - -## Create directory for additional log files -LOG_PATH=/psc/wrk/logs -mkdir -p ${LOG_PATH} - -## Start PASOE server -cd ${WRKDIR} -${DLC}/bin/pasman create -v -Z pas -u admin:admin ${INSTANCE_NAME} -${INSTANCE_NAME}/bin/tcman.sh import -v /deploy/artifacts/ablapps/Sports.oear -${INSTANCE_NAME}/bin/tcman.sh start - -cd /deploy/scripts -# ant -f ./build.xml -DINSTANCE.ARCHIVE.FILE.ROOT=../artifacts/ deployAndStartInstance -l ${LOG_PATH}/start-server.log - -# SIGTERM is issued by Docker commands (docker stop or docker-compose down) -# Adding a trap for the SIGTERM signal. i.e., to stop PASOE before stopping container -# not_done is a temporary flag -trap 'rm /tmp/not_done; ant -f ./build.xml stopInstance; sleep 10' SIGTERM - -wait_for_sigterm() { - # Loop until not_done flag is removed - touch /tmp/not_done - while [ -f /tmp/not_done ] - do - sleep 60 & - wait $! # This is required such that a trap on main process is triggered - done -} - -## Start FluentBit, if 'FLUENTBIT_LOGGING=true' -if [ "${FLUENTBIT_LOGGING}" = "true" ] -then - export FLUENT_CONFIG_PATH=/etc/fluent-bit - sh /deploy/scripts/setup-fluentbit.sh - /usr/local/bin/fluent-bit -c ${FLUENT_CONFIG_PATH}/fluent-bit.conf -l ${LOG_PATH}/fluent-bit.log & - wait_for_sigterm -else - wait_for_sigterm -fi \ No newline at end of file diff --git a/SportsApp/deploy/undeploy.sh b/SportsApp/deploy/undeploy.sh deleted file mode 100644 index 4136638..0000000 --- a/SportsApp/deploy/undeploy.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -# undeploy -PAS_INSTANCE_NAME=oepas1 -docker-compose -p ${PAS_INSTANCE_NAME} down -v -echo "Removed '${PAS_INSTANCE_NAME}_dc'" \ No newline at end of file diff --git a/SportsApp/gradle/wrapper/gradle-wrapper.jar b/SportsApp/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL diff --git a/SportsApp/gradle/wrapper/gradle-wrapper.properties b/SportsApp/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 2e6e589..0000000 --- a/SportsApp/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/SportsApp/gradlew b/SportsApp/gradlew deleted file mode 100644 index c53aefa..0000000 --- a/SportsApp/gradlew +++ /dev/null @@ -1,234 +0,0 @@ -#!/bin/sh - -# -# Copyright 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions $var, ${var}, ${var:-default}, ${var+SET}, -# ${var#prefix}, ${var%suffix}, and $( cmd ); -# * compound commands having a testable exit status, especially case; -# * various built-in commands including command, set, and ulimit. -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/SportsApp/gradlew.bat b/SportsApp/gradlew.bat deleted file mode 100644 index 107acd3..0000000 --- a/SportsApp/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/SportsApp/settings.gradle b/SportsApp/settings.gradle deleted file mode 100644 index 16a4617..0000000 --- a/SportsApp/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/7.3.3/userguide/multi_project_builds.html - */ - -rootProject.name = 'OpenEdgeOps' -include "Sports" \ No newline at end of file diff --git a/SportsApp/webui/grid.js b/SportsApp/webui/grid.js deleted file mode 100644 index e6c9df0..0000000 --- a/SportsApp/webui/grid.js +++ /dev/null @@ -1,76 +0,0 @@ -/*global alert, $, progress, require */ - -// require("@progress/jsdo"); - -$(function () { - 'use strict'; - var serviceURI = "https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports"; - var catalogURI = serviceURI + "/static/SportsService.json"; - - function createGrid() { - console.log("DEBUG: createGrid(): "); - $('#grid').kendoGrid({ - dataSource: { - serverFiltering: false, - serverSorting: false, - serverPaging: false, - type: "jsdo", - transport: { - jsdo: "Customer" - }, - error: function (e) { - var messages = ""; - if (e.errorThrown) { - messages += e.errorThrown.message + "\n"; - } - e.sender.transport.jsdo.getErrors().forEach(function (err) { - messages += err.error + "\n"; - }); - alert("Error: \n" + messages); - } - }, - selectable: "multiple row", - navigatable: true, - filterable: true, - height: 400, - groupable: true, - reorderable: true, - resizable: true, - sortable: true, - pageable: { - refresh: true, - pageSizes: true, - pageSize: 10, - buttonCount: 5 - }, - editable: 'inline', - toolbar: ['create'], - columns: [ - { - field: 'CustNum', - title: 'Cust Num', - width: 100 - }, - {field: 'Name'}, - {field: 'State'}, - {field: 'Country'}, - {command: ['edit', 'destroy'], title: ' ', width: '250px'} - ] - }); - } - - try { - // Create a new session object - progress.data.getSession({ - serviceURI: serviceURI, - catalogURI: catalogURI, - authenticationModel: "anonymous" - }).then(function (/* jsdosession, result, info */) { - createGrid(); - }, function (/* jsdosession, result, info */) { - alert("Error while creating session."); - }); - } catch (e) { - alert("Error instantiating objects: " + e); - } -}); diff --git a/SportsApp/webui/index.html b/SportsApp/webui/index.html deleted file mode 100644 index 7eee979..0000000 --- a/SportsApp/webui/index.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - PUG Challenge 2023! - - - - - - - - - - - - - - -

- -
-

SportsApp

-

Customer Management

-
-
- -
-
-

Powered by © Progress Software Corporation

-
-
- - - \ No newline at end of file diff --git a/SportsApp/webui/progress.all.js b/SportsApp/webui/progress.all.js deleted file mode 100644 index 5082570..0000000 --- a/SportsApp/webui/progress.all.js +++ /dev/null @@ -1,14881 +0,0 @@ -/* -Progress JSDO Version: 4.4.1 - -Copyright 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. -*/ -/* -progress.util.js Version: 4.4.0-7 - -Copyright (c) 2014-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -Contains support objects used by the jsdo and/or session object - -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. - - */ -/*global progress:true*/ -/*jslint nomen: true*/ -(function () { - - /* Define these if not defined yet - they may already be defined if - * progress.js was included first */ - if (typeof progress === "undefined") { - progress = {}; - } - - if (typeof progress.data === "undefined") { - progress.data = {}; - } - - progress.util = {}; - - var STRING_OBJECT_TYPE = "String", - DATE_OBJECT_TYPE = "Date", - CHARACTER_ABL_TYPE = "CHARACTER"; - - - /** - * Utility class that allows subscribing and unsubscribing from named events. - * - * @returns {progress.util.Observable} - */ - progress.util.Observable = function () { - /* - * Example format of the events object. Some event delegates may only - * have a function setup, others may optionally have scope, and possibly an operation filter - * - * var events = { - * afterfill : [{ - * scope : {}, // this is optional - * fn : function () {}, - * operation : 'getCustomers' // this is optional - * }, ...] - * - * } - * - * - * - */ - - /* - * remove the given function from the array of observers - */ - function _filterObservers(observers, fn, scope, operation) { - return observers.filter(function (el) { - if (el.fn !== fn || el.scope !== scope || el.operation !== operation) { - return el; - } - }, this); - } - - /* - * validate the arguments passed to the subscribe function - */ - this.validateSubscribe = function (args, evt, listenerData) { - - if (args.length >= 2 && (typeof args[0] === 'string') && (typeof args[1] === 'string')) { - listenerData.operation = args[1]; - listenerData.fn = args[2]; - listenerData.scope = args[3]; - - } else if (args.length >= 2 && (typeof args[0] === 'string') && (typeof args[1] === 'function')) { - listenerData.operation = undefined; - listenerData.scope = args[2]; - listenerData.fn = args[1]; - } else { - throw new Error(); - } - - }; - - - /* - * bind the specified function so it receives callbacks when the - * specified event name is called. Event name is not case sensitive. - * An optional scope can be provided so that the function is executed - * in the given scope. If no scope is given, then the function will be - * called without scope. - * - * If the same function is registered for the same event a second time with - * the same scope the original subscription is removed and replaced with the new function - * to be called in the new scope. - * - * This method has two signatures. - * - * Signature 1: - * @param evt The name of the event to bind a handler to. String. Not case sensitive. - * @param fn The function callback for the event . Function. - * @param scope The scope the function is to be run in. Object. Optional. - * - * Signature 2: - * - * @param evt The name of the event to bind a handler to. String. Not case sensitive - * @param operation The name of the operation to bind to. String. Case sensitive. - * @param fn The function callback for the event . Function. - * @param scope The scope the function is to be run in. Object. Optional. - - */ - this.subscribe = function (evt, operation, fn, scope) { - var listenerData, - observers; - - if (!evt) { - throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "subscribe")); - } - - if (typeof evt !== 'string') { - throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), - "subscribe", progress.data._getMsgText("jsdoMSG039"))); - } - - this._events = this._events || {}; - evt = evt.toLowerCase(); - listenerData = {fn: undefined, scope: undefined, operation: undefined}; - - try { - this.validateSubscribe(arguments, evt, listenerData); - } catch (e) { - throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), - "subscribe", e.message)); - } - - observers = this._events[evt] || []; - - // make sure we don't add duplicates - observers = _filterObservers(observers, listenerData.fn, - listenerData.scope, listenerData.operation); - observers.push(listenerData); - this._events[evt] = observers; - - return this; - }; - - /* - * remove the specified function so it no longer receives events from - * the given name. event name is not case sensitive. - * - * This method has two signaturues. - * Signature 1: - * @param evt Required. The name of the event for which to unbind the given function. String. - * @param fn Required. The function to remove from the named event. Function. - * @param scope Optional. The function scope in which to remove the listener. Object. - * - * Signature 2: - * - * @param evt Required. The name of the event for which to unbind the given function. - String. Not case sensitive - * @param operation Required. The name of the operation to receive events. String. Case Sensitive - * @param fn Required. The function to remove from the named event. Function. - * @param scope Optional. The function scope in which to remove the listener. Object. - * - */ - this.unsubscribe = function (evt, operation, fn, scope) { - var listenerData, - observers; - - if (!evt) { - throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "unsubscribe")); - } - - if (typeof evt !== 'string') { - throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), - "unsubscribe", progress.data._getMsgText("jsdoMSG037"))); - } - - this._events = this._events || {}; - evt = evt.toLowerCase(); - listenerData = {fn: undefined, scope: undefined, operation: undefined}; - try { - this.validateSubscribe(arguments, evt, listenerData); - } catch (e) { - // throw new Error("Invalid signature for unsubscribe. " + e.message); - throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), - "unsubscribe", e.message)); - } - - observers = this._events[evt] || []; - if (observers.length > 0) { - this._events[evt] = _filterObservers(observers, listenerData.fn, - listenerData.scope, listenerData.operation); - } - - return this; - }; - - /* - * trigger an event of the given name, and pass the specified data to - * the subscribers of the event. Event name is not case sensitive. - * A variable numbers of arguments can be passed as arguments to the event handler. - * - * This method has two signatures - * Signature 1: - * @param evt The name of the event to fire. String. Not case sensitive. - * @param operation The name of the operation. String. Case sensitive - * @param args Optional. A variable number of arguments to pass to the event handlers. - * - * Signature 2: - * @param evt The name of the event to fire. String. Not case sensitive - * @param args Optional. A variable number of arguments to pass to the event handlers. - */ - this.trigger = function (evt, operation, args) { - var observers, - op; - - if (!evt) { - throw new Error(progress.data._getMsgText("jsdoMSG037", this.toString(), "trigger")); - } - - this._events = this._events || {}; - evt = evt.toLowerCase(); - observers = this._events[evt] || []; - if (observers.length > 0) { - args = Array.prototype.slice.call(arguments); - - if ((arguments.length >= 2) - && (typeof evt === 'string') - && (typeof operation === 'string')) { - // in alt format the second argument is the event name, - // and the first is the operation name - op = operation; - args = args.length > 2 ? args.slice(2) : []; - } else if (arguments.length >= 1 && (typeof evt === 'string')) { - op = undefined; - args = args.length > 1 ? args.slice(1) : []; - } else { - throw new Error(progress.data._getMsgText("jsdoMSG033", this.toString(), "trigger")); - } - - observers.forEach(function (el) { - if (el.operation === op) { - el.fn.apply(el.scope, args); - } - }); - - } - - return this; - }; - - // unbind all listeners from the given event. If the - // evt is undefined, then all listeners for all events are unbound - // evnt name is not case sensitive - // @param evt Optional. The name of the event to unbind. If not passed, then all events are unbound - this.unsubscribeAll = function (evt, operation) { - var observers; - - if (evt) { - this._events = this._events || {}; - if (typeof evt === 'string') { - evt = evt.toLowerCase(); - observers = this._events[evt] || []; - - observers.forEach(function (el) { - if (el.operation) { - this.unsubscribe(evt, el.operation, el.fn, el.scope); - } else { - this.unsubscribe(evt, el.fn, el.scope); - } - }, this); - } - } else { - this._events = {}; - } - - return this; - }; - }; - - - /** - * Utility class that saves/reads data to localStorage - * - * @returns {progress.data.LocalStorage} - */ - progress.data.LocalStorage = function LocalStorage() { - - /*global localStorage */ - if (typeof localStorage === "undefined") { - // "progress.data.LocalStorage: No support for localStorage." - throw new Error(progress.data._getMsgText("jsdoMSG126", "progress.data.LocalStorage", "localStorage")); - } - - - // "Methods" - - this.saveToLocalStorage = function (name, dataObj) { - localStorage.setItem(name, JSON.stringify(dataObj)); - }; - - this.readFromLocalStorage = function (name) { - - var jsonStr = localStorage.getItem(name), - dataObj = null; - - if (jsonStr !== null) { - try { - dataObj = JSON.parse(jsonStr); - } catch (e) { - dataObj = null; - } - } - return dataObj; - }; - - this.clearLocalStorage = function (name) { - localStorage.removeItem(name); - }; - - }; // End of LocalStorage - - - ///////////////////////////////////////////////////////////////////////////////////////// - // Utility Functions - - /* - * Converts the specified filter object to an OpenEdge ABL Where String. - * - * @param tableRef - handle to the table in jsdo, where string is applied to. - * @param filter - the filter object to convert. - * - * @returns - translated OE where string. - */ - progress.util._convertToABLWhereString = function (tableRef, filter) { - var result = [], - logic = filter.logic || "and", - idx, - length, - field, - fieldInfo, - type, - format, - operator, - value, - ablType, - //filters = (filter.filters) ? filter.filters : [filter], - filters = filter.filters || [filter], - - whereOperators = { - eq: "=", - neq: "<>", - gt: ">", - gte: ">=", - lt: "<", - lte: "<=", - contains : "INDEX", - doesnotcontain: "INDEX", - endswith: "R-INDEX", - startswith: "BEGINS", - isnull: "ISNULL", - isnotnull: "ISNOTNULL", - isempty: "ISEMPTY", - isnotempty: "ISNOTEMPTY" - }; - - for (idx = 0, length = filters.length; idx < length; idx += 1) { - filter = filters[idx]; - field = filter.field; - value = filter.value; - - if (filter.filters) { - filter = progress.util._convertToABLWhereString(tableRef, filter); - } else { - // Use original field name instead of serialized name - if (field && tableRef._name) { - fieldInfo = tableRef._jsdo[tableRef._name]._fields[field.toLowerCase()]; - if (fieldInfo && fieldInfo.origName) { - field = fieldInfo.origName; - } - } - - operator = whereOperators[filter.operator]; - - if (operator === undefined) { - throw new Error("The operator " + filter.operator + " is not valid."); - } - - switch (filter.operator) { - case "isnull": - case "isnotnull": - case "isempty": - case "isnotempty": - value = undefined; - break; - } - - if (operator && value !== undefined) { - type = progress.util._getObjectType(value); - - // We need to build a template format string for the where string. - // We'll first add positional info for the value - if (type === STRING_OBJECT_TYPE) { - format = "'{1}'"; - value = value.replace(/'/g, "~'"); - } else if (type === DATE_OBJECT_TYPE) { - ablType = tableRef._getABLType(field); - if (ablType === "DATE") { - format = "DATE({1:MM, dd, yyyy})"; - } else if (ablType === "DATETIME-TZ") { - // zzz here means to translate timezone offset into minutes - format = "DATETIME-TZ({1:MM, dd, yyyy, hh, mm, ss, fff, zzz})"; - } else { - format = "DATETIME({1:MM, dd, yyyy, hh, mm, ss, fff})"; - } - } else { - format = "{1}"; - } - - // Most where strings are in the format: field operator value. Ex. custnum < 100 - // An exception to this is INDEX() and R-INDEX() which have format: operator field value - // Ex. R-INDEX(name, "LTD") - if (operator === "INDEX" || operator === "R-INDEX") { - if (type !== STRING_OBJECT_TYPE) { - throw new Error("Error parsing filter object. The operator " + filter.operator + - " requires a string value"); - } - if (filter.operator === "doesnotcontain") { - format = "{0}(" + "{2}, " + format + ") = 0"; - } else if (filter.operator === "contains") { - format = "{0}(" + "{2}, " + format + ") > 0"; - } else { // else filter.operator = "endswith" - format = "{2} MATCHES '*{1}'"; - } - } else { - format = "{2} {0} " + format; - } - - filter = progress.util._format(format, operator, value, field); - } else if (operator && value === undefined) { - if (filter.operator === "isempty" || filter.operator === "isnotempty") { - ablType = tableRef._getABLType(field); - if (ablType !== CHARACTER_ABL_TYPE) { - throw new Error("Error parsing filter object. The operator " + filter.operator + - " requires a CHARACTER field"); - } - if (filter.operator === "isempty") { - format = "{2} = ''"; - } else if (filter.operator === "isnotempty") { - format = "{2} <> ''"; - } - } else { - if (filter.operator === "isnull") { - format = "{2} = ?"; - } else if (filter.operator === "isnotnull") { - format = "{2} <> ?"; - } else { - format = "{2} {0} ?"; - } - } - - // format, operator {0}, value {1}, field {2} - filter = progress.util._format(format, operator, value, field); - } - } - - result.push(filter); - } - - filter = result.join(" " + logic + " "); - - if (result.length > 1) { - filter = "(" + filter + ")"; - } - - return filter; - }; - - - /* - * Converts the specified filter object to an SQL Query String. - * - * @param tableName - tableName of table in jsdo, where clause is applied to. - * @param filter - the filter object to convert. - * - * @returns - translated SQL where clause. - */ - progress.util._convertToSQLQueryString = function (tableRef, filter, addSelect) { - var result = [], - logic = filter.logic || "and", - idx, - length, - field, - type, - format, - operator, - value, - fieldFormat, - filters = filter.filters || [filter], - filterStr, - usingLike = true, - - whereOperators = { - eq: "=", - neq: "!=", - gt: ">", - gte: ">=", - lt: "<", - lte: "<=", - contains : "LIKE", - doesnotcontain: "NOT LIKE", - endswith: "LIKE", - startswith: "LIKE", - isnull: "ISNULL", - isnotnull: "ISNOTNULL", - isempty: "ISEMPTY", - isnotempty: "ISNOTEMPTY" - }; - - if (typeof addSelect === "undefined") { - addSelect = false; - } - - for (idx = 0, length = filters.length; idx < length; idx += 1) { - filter = filters[idx]; - field = filter.field; - value = filter.value; - - if (filter.filters) { - filterStr = progress.util._convertToSQLQueryString(tableRef, filter, false); - } else { - operator = whereOperators[filter.operator]; - - if (operator === undefined) { - throw new Error("The operator " + filter.operator + " is not valid."); - } - - switch (filter.operator) { - case "isnull": - case "isnotnull": - case "isempty": - case "isnotempty": - value = undefined; - break; - } - - if (operator && value !== undefined) { - type = progress.util._getObjectType(value); - - if (operator === "LIKE" || operator === "NOT LIKE") { - if (type !== STRING_OBJECT_TYPE) { - throw new Error("Error parsing filter object. The operator " + filter.operator + - " requires a string value"); - } - } - - if (type === STRING_OBJECT_TYPE) { - format = "'{1}'"; - value = value.replace(/'/g, "''"); - } else if (type === DATE_OBJECT_TYPE) { - fieldFormat = tableRef._getFormat(field); - if (fieldFormat === "date") { - format = "'{1:yyyy-MM-dd}'"; - } else if (fieldFormat === "date-time") { - format = "{1:#ISO(iso)}"; - } else if (fieldFormat === "time") { - format = "'{1:FFF}'"; - } - } else { - format = "{1}"; - } - - // We need to build a template format string for the where string. - // We'll first add positional info for the value, which is represented by {1} - if (filter.operator === "startswith") { - format = "'{1}%'"; - } else if (filter.operator === "endswith") { - format = "'%{1}'"; - } else if (filter.operator === "contains" || filter.operator === "doesnotcontain") { - format = "'%{1}%'"; - } else { - usingLike = false; - } - - if (usingLike) { - value = value.replace(/%/g, '\\%'); - value = value.replace(/_/g, '\\_'); - } - - format = "{2} {0} " + format; - filterStr = progress.util._format(format, operator, value, field); - } else if (operator && value === undefined) { - if (filter.operator === "isempty" || filter.operator === "isnotempty") { - type = tableRef._fields[field.toLowerCase()].type; - if (type !== STRING_OBJECT_TYPE.toLowerCase()) { - throw new Error("Error parsing filter object. The operator " + filter.operator + - " requires a string field"); - } - if (filter.operator === "isempty") { - format = "{2} = ''"; - } else if (filter.operator === "isnotempty") { - format = "{2} != ''"; - } - } else { - if (filter.operator === "isnull") { - format = "{2} IS NULL"; - } else if (filter.operator === "isnotnull") { - format = "{2} IS NOT NULL"; - } else { - format = "{2} {0} NULL"; - } - } - - // format, operator {0}, value {1}, field {2} - filterStr = progress.util._format(format, operator, value, field); - } - } - - result.push(filterStr); - } - - filterStr = result.join(" " + logic + " "); - - if (result.length > 1) { - filterStr = "(" + filterStr + ")"; - } - - if (addSelect === true) { - filterStr = "SELECT * FROM " + tableRef._name + " WHERE " + filterStr; - } - - return filterStr; - }; - - - /* - * Returns the object type; Example "String", "Date" - * Constants for object type values are defined above. - * - * @param value - the object whose type is returned - */ - progress.util._getObjectType = function (value) { - // Returns [object xxx]. Removing [object ] - return Object.prototype.toString.call(value).slice(8, -1); - }; - - - /* - * Substitutes in a variable number of arguments into specified format string (with place-holders) - * - * @param fmt - the format string with place-holders, eg. "{0} text {1}". - * - * @returns - formatted string. - */ - progress.util._format = function (fmt) { - /*jslint regexp: true*/ - var values = arguments, - formatRegExp = /\{(\d+)(:[^\}]+)?\}/g; - /*jslint regexp: false*/ - - return fmt.replace(formatRegExp, function (match, index, placeholderFormat) { - var value = values[parseInt(index, 10) + 1]; - - return progress.util._toString(value, placeholderFormat ? placeholderFormat.substring(1) : ""); - }); - - }; - - /* - * Converts the specified value param to a string. - * - * @param value - object to convert - * @param fmt - optional format string with place-holders, eg. "MM dd yyyy". - * - * @returns - converted string. - */ - progress.util._toString = function (value, fmt) { - var str; - - if (fmt) { - if (progress.util._getObjectType(value) === "Date") { - return progress.util._formatDate(value, fmt); - } - } - - if (typeof value === "number") { - str = value.toString(); - } else { - str = (value !== undefined ? value : ""); - } - - return str; - }; - - /* - * Accepts string representing number and optionally pads it with "0"'s to conform to - * specified number of digits. - * - * @param number - string representing number to pad. - * @param digit - number of digits desired for padded string. If not specified, default is 2. - * - * @returns - padded string representing number. - */ - progress.util._pad = function (number, digits) { - var zeros = ["", "0", "00", "000", "0000"], - end; - - number = String(number); - digits = digits || 2; - end = digits - number.length; - - if (end) { - return zeros[digits].substring(0, end) + number; - } - return number; - }; - - /* - * Converts the specified date param to a string. - * - * @param date - date object to convert - * @param fmt - format string with place-holders, eg. "MM dd yyyy". - * - * @returns - converted string. - */ - progress.util._formatDate = function (date, format) { - /*jslint regexp: true*/ - var dateFormatRegExp = - /dd|MM|yyyy|hh|mm|fff|FFF|ss|zzz|iso|"[^"]*"|'[^']*'/g; - /*jslint regexp: false*/ - - return format.replace(dateFormatRegExp, function (match) { - var minutes, - result, - sign; - - if (match === "dd") { - result = progress.util._pad(date.getDate()); - } else if (match === "MM") { - result = progress.util._pad(date.getMonth() + 1); - } else if (match === "yyyy") { - result = progress.util._pad(date.getFullYear(), 4); - } else if (match === "hh") { - result = progress.util._pad(date.getHours()); - } else if (match === "mm") { - result = progress.util._pad(date.getMinutes()); - } else if (match === "ss") { - result = progress.util._pad(date.getSeconds()); - } else if (match === "fff") { - result = progress.util._pad(date.getMilliseconds(), 3); - } else if (match === "FFF") { - result = String(date.getTime()); - } else if (match === "zzz") { - // timezone is returned in minutes - minutes = date.getTimezoneOffset(); - sign = minutes < 0; - result = (sign ? "+" : "-") + minutes; - } else if (match === "iso") { - result = date.toISOString(); - } - - return result !== undefined ? result : match.slice(1, match.length - 1); - }); - }; - - /* - * Processes settings in a jsdoSettings object. - * This method is used by project templates. - */ - progress.util.jsdoSettingsProcessor = function jsdoSettingsProcessor(jsdoSettings) { - if (typeof jsdoSettings === 'object') { - if (jsdoSettings.authenticationModel === undefined || jsdoSettings.authenticationModel === "") { - jsdoSettings.authenticationModel = "ANONYMOUS"; - } - } - }; - -}()); -//# sourceURL=progress.jsdo.js -/* -progress.js Version: 4.4.1-01 - -Copyright (c) 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - // "use strict"; - - var PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS = 20, - PROGRESS_JSDO_OP_STRING = ["none", "create", "read", "update", "delete", "submit"], - PROGRESS_JSDO_ROW_STATE_STRING = ["", "created", "", "modified", "deleted"]; - - /* define these if not defined yet - they may already be defined if - progress.session.js was included first */ - if (typeof progress === 'undefined') { - progress = {}; - } - if (typeof progress.data === 'undefined') { - progress.data = {}; - } - - progress.data._nextid = 0; - progress.data._uidprefix = "" + ( Date.now ? Date.now() : (new Date().getTime())); - - /* 15 - 9 */ - var UID_MAX_VALUE = 999999999999999; - - progress.data._getNextId = function () { - var uid = ++progress.data._nextid; - if (uid >= UID_MAX_VALUE) { - progress.data._nextid = uid = 1; - progress.data._uidprefix = "" + ( Date.now ? Date.now() : (new Date().getTime())); - } - - return progress.data._uidprefix + "-" + uid; - }; - - - var msg = {}; - msg.msgs = {}; -// msg numbers 0 - 99 are related to use of the API (methods and properties we expose to developers) -// 100 - 109 relate to network errors -// 110 - 998 are for miscellaneous errors - - msg.msgs.jsdoMSG000 = "JSDO, Internal Error: {1}"; - msg.msgs.jsdoMSG001 = "JSDO: JSDO has multiple tables. Please use {1} at the table reference level."; - msg.msgs.jsdoMSG002 = "JSDO: Working record for '{1}' is undefined."; - msg.msgs.jsdoMSG003 = "JSDO: {1} function requires a function as a parameter."; - msg.msgs.jsdoMSG004 = "JSDO: Unable to find resource '{1}' in the catalog."; - msg.msgs.jsdoMSG005 = "JSDO: Data for table '{1}' was not specified in addRecords() call."; - msg.msgs.jsdoMSG006 = "JSDO: Data for JSDO was not specified in addRecords() call."; - msg.msgs.jsdoMSG007 = "JSDO: Test function in {1} must return a boolean."; - msg.msgs.jsdoMSG008 = "JSDO: Invalid keyFields parameter in addRecords() call."; - msg.msgs.jsdoMSG009 = "JSDO: KeyField '{1}' in addRecords() call was not found in the schema."; - msg.msgs.jsdoMSG010 = "JSDO: Field '{1}' in relationship was not found in the schema."; - msg.msgs.jsdoMSG011 = "UIHelper: JSDO has multiple tables. " + - "Please use {1} at the table reference level."; - msg.msgs.jsdoMSG012 = "UIHelper: Invalid {2} parameter in {1} call."; - msg.msgs.jsdoMSG020 = "JSDO: tableName parameter must be a string in addRecords() call."; - msg.msgs.jsdoMSG021 = "JSDO: addMode parameter must be specified in addRecords() call."; - msg.msgs.jsdoMSG022 = "JSDO: Invalid addMode specified in addRecords() call."; - msg.msgs.jsdoMSG023 = "JSDO: Duplicate found in addRecords() call using APPEND mode."; - msg.msgs.jsdoMSG024 = "{1}: Unexpected signature in call to {2} function."; - msg.msgs.jsdoMSG025 = "{1}: Invalid parameters in call to {2} function."; - msg.msgs.jsdoMSG026 = "JSDO: saveChanges requires a " + - "CREATE, UPDATE, DELETE or SUBMIT operation to be defined."; - msg.msgs.jsdoMSG030 = "JSDO: Invalid {1}, expected {2}."; - msg.msgs.jsdoMSG031 = "JSDO: Specified sort field name '{1}' was not found in the schema."; - msg.msgs.jsdoMSG032 = "JSDO: Before-image data already exists for record in addRecords() call."; - msg.msgs.jsdoMSG033 = "{1}: Invalid signature for {2}. {3}"; - msg.msgs.jsdoMSG034 = "JSDO: In '{1}' function, JSON data is missing _id"; - msg.msgs.jsdoMSG035 = "JSDO: In '{1}' function, before-image JSON data is missing prods:clientId"; - msg.msgs.jsdoMSG036 = "JSDO: '{1}' can only be called for a dataset"; - msg.msgs.jsdoMSG037 = "{1}: Event name must be provided for {2}."; - msg.msgs.jsdoMSG038 = "Too few arguments. There must be at least {1}."; - msg.msgs.jsdoMSG039 = "The name of the event is not a string."; - msg.msgs.jsdoMSG040 = "The event listener is not a function."; - msg.msgs.jsdoMSG041 = "The event listener scope is not an object."; - msg.msgs.jsdoMSG042 = "'{1}' is not a defined event for this object."; - msg.msgs.jsdoMSG043 = "{1}: A session object was requested to check the status of a Mobile " + - "Service named '{2}', but it has not loaded the definition of that service."; - msg.msgs.jsdoMSG044 = "JSDO: In '{1}' function, {2} is missing {3} property."; - msg.msgs.jsdoMSG045 = "JSDO: {1} function: {2} is missing {3} property."; - msg.msgs.jsdoMSG046 = "JSDO: {1} operation is not defined."; - msg.msgs.jsdoMSG047 = "{1} timeout expired."; - msg.msgs.jsdoMSG048 = "{1}: {2} method has argument '{3}' that is missing property '{4}'."; - msg.msgs.jsdoMSG049 = "{1}: Unexpected error calling {2}: {3}"; - msg.msgs.jsdoMSG050 = "No token returned from server"; - msg.msgs.jsdoMSG051 = "{1} The login method was not executed because the AuthenticationProvider is already logged in."; - msg.msgs.jsdoMSG052 = "{1}: The login method was not executed because no credentials were supplied."; - msg.msgs.jsdoMSG053 = "{1}: {2} was not executed because the AuthenticationProvider is not logged in."; - msg.msgs.jsdoMSG054 = "{1}: Token refresh was not executed because the AuthenticationProvider does not have a refresh token."; - msg.msgs.jsdoMSG055 = "{1}: Token refresh was not executed because the authentication model is not sso."; - msg.msgs.jsdoMSG056 = "{1}: Already logged in."; - msg.msgs.jsdoMSG057 = "{1}: Cannot call {2} when authenticationModel is SSO. Please use the AuthenticationProvider object instead."; - msg.msgs.jsdoMSG058 = "{1}: Cannot pass username and password to addCatalog when authenticationModel " + - "is sso. Pass an AuthenticationProvider instead."; - msg.msgs.jsdoMSG059 = "{1}: Error in constructor. The authenticationModels of the " + - "AuthenticationProvider ({2}) and the JSDOSession ({3}) were not compatible."; - msg.msgs.jsdoMSG060 = "AuthenticationProvider: AuthenticationProvider is no longer logged in. " + - "Tried to refresh SSO token but failed due to authentication error at token server."; - msg.msgs.jsdoMSG061 = "{1}: Attempted to set {2} property to an invalid value."; - - // 100 - 109 relate to network errors - msg.msgs.jsdoMSG100 = "JSDO: Unexpected HTTP response. Too many records."; - msg.msgs.jsdoMSG101 = "Network error while executing HTTP request."; - - // 110 - 499 are for miscellaneous errors - msg.msgs.jsdoMSG110 = "Catalog error: idProperty not specified for resource '{1}'. " + - "idProperty is required {2}."; - msg.msgs.jsdoMSG111 = "Catalog error: Schema '{1}' was not found in catalog."; - msg.msgs.jsdoMSG112 = "Catalog error: Output parameter '{1}' was not found for operation '{2}'."; - msg.msgs.jsdoMSG113 = "Catalog error: Found xType '{1}' for output parameter '{2}' " + - "for operation '{3}' but xType DATASET, TABLE or ARRAY was expected."; - msg.msgs.jsdoMSG114 = "JSDO: idProperty '{1}' is missing from '{2}' record."; - msg.msgs.jsdoMSG115 = "JSDO: Invalid option specified in {1}() call."; - msg.msgs.jsdoMSG116 = "JSDO: {1} parameter must be a string in {2} call."; - msg.msgs.jsdoMSG117 = "JSDO: Schema from storage area '{1}' does not match JSDO schema"; - msg.msgs.jsdoMSG118 = "JSDO: Plugin '{1}' was not found."; - msg.msgs.jsdoMSG119 = "JSDO: A mappingType is expected when 'capabilities' is set." + - " Please specify a plugin (ex: JFP)."; - msg.msgs.jsdoMSG120 = "JSDO: Parameter '{2}' requires capability '{1}' in the catalog."; - msg.msgs.jsdoMSG121 = "{1}: Argument {2} must be of type {3} in {4} call."; - msg.msgs.jsdoMSG122 = "{1}: Incorrect number of arguments in {2} call. There should be {3}."; - msg.msgs.jsdoMSG123 = "{1}: A server response included an invalid '{2}' header."; - msg.msgs.jsdoMSG124 = "JSDO: autoApplyChanges is not supported for saveChanges(true) " + - "with a temp-table. Use jsdo.autoApplyChanges = false."; - msg.msgs.jsdoMSG125 = "{1}: The AuthenticationProvider is not managing valid credentials."; - msg.msgs.jsdoMSG126 = "{1}: No support for {2}."; - msg.msgs.jsdoMSG127 = "JSDO: acceptRowChanges() cannot be called for record with _rejected === true."; - - // 500 - 998 are for generic errors - msg.msgs.jsdoMSG500 = "{1}: '{2}' objects must contain a '{3}' property."; - msg.msgs.jsdoMSG501 = "{1}: '{2}' in '{3}' function cannot be an empty string."; - msg.msgs.jsdoMSG502 = "{1}: The '{2}' parameter passed to the '{3}' function has an invalid value for " + - "its '{4}' property."; - msg.msgs.jsdoMSG503 = "{1}: '{2}' must be of type '{3}'."; - msg.msgs.jsdoMSG504 = "{1}: {2} has an invalid value for the '{3}' property."; - msg.msgs.jsdoMSG505 = "{1}: '{2}' objects must have a '{3}' method."; - // use message below if invalid parameter value is an object - msg.msgs.jsdoMSG506 = "{1}: Invalid argument for the {2} parameter in {3} call."; - // use message below if invalid parameter value is a primitive - msg.msgs.jsdoMSG507 = "{1}: '{2}' is an invalid value for the {3} parameter in {4} call."; - msg.msgs.jsdoMSG508 = "JSDOSession: If a JSDOSession object is using the SSO authentication model, " + - "the options object passed to its constructor must include an authProvider property."; - msg.msgs.jsdoMSG509 = "progress.data.getSession: If the authenticationModel is AUTH_TYPE_SSO, " + - "authenticationURI and authProviderAuthenticationModel are required parameters."; - msg.msgs.jsdoMSG510 = "{1}: This session has been invalidated and cannot be used."; - msg.msgs.jsdoMSG511 = "JSDOSession: addCatalog() can only be called if an AuthenticationProvider was passed as an argument or " + - "connect() has been successfully called."; - - msg.msgs.jsdoMSG998 = "JSDO: JSON object in addRecords() must be DataSet or Temp-Table data."; - - msg.getMsgText = function (n, args) { - var text = msg.msgs[n], - i; - if (!text) { - throw new Error("Message text was not found by getMsgText()"); - } - for (i = 1; i < arguments.length; i += 1) { - text = text.replace(new RegExp('\\{' + i + '\\}', 'g'), arguments[i]); - } - - return text; - }; - - progress.data._getMsgText = msg.getMsgText; - - progress.data.PluginManager = {}; - progress.data.PluginManager._plugins = {}; - - progress.data.PluginManager.addPlugin = function(name, plugin) { - if (progress.data.PluginManager._plugins[name] === undefined) { - progress.data.PluginManager._plugins[name] = plugin; - } - else { - throw new Error("A plugin named '" + name + "' is already registered."); - } - }; - - progress.data.PluginManager.getPlugin = function (name) { - return progress.data.PluginManager._plugins[name]; - }; - - progress.data.JSIndexEntry = function JSIndexEntry(index) { - this.index = index; - }; - - progress.data.JSTableRef = function JSTableRef(jsdo, tableName) { - this._jsdo = jsdo; - this._name = tableName; - this._schema = null; - this._primaryKeys = null; - this._fields = null; - this._processed = {}; - this._visited = false; - - // record is used to represent the current record for a table reference - this.record = null; - - // Data structure - this._data = []; - this._index = {}; - this._hasEmptyBlocks = false; - - // Arrays to keep track of changes - this._beforeImage = {}; - this._added = []; - this._changed = {}; - this._deleted = []; - this._lastErrors = []; - this._convertForServer; - - this._createIndex = function () { - var i, block, id, idProperty; - this._index = {}; - this._hasEmptyBlocks = false; - for (i = 0; i < this._data.length; i += 1) { - block = this._data[i]; - if (!block) { - this._hasEmptyBlocks = true; - continue; - } - id = this._data[i]._id; - if (!id) { - idProperty = this._jsdo._resource.idProperty; - if (typeof(idProperty) == "string") { - id = this._data[i][idProperty]; - if (!id) { - throw new Error(msg.getMsgText("jsdoMSG114", idProperty, this._name)); - } - id += ""; - } - else { - id = progress.data._getNextId(); - } - this._data[i]._id = id; - } - this._index[id] = new progress.data.JSIndexEntry(i); - } - this._needCompaction = false; - }; - - this._compact = function () { - var newDataArray = [], i, block; - - for (i = 0; i < this._data.length; i += 1) { - block = this._data[i]; - if (block) { - newDataArray.push(block); - } - } - this._data = newDataArray; - this._createIndex(); - }; - - this._loadBeforeImageData = function (jsonObject, beforeImageJsonIndex, keyFields) { - var prodsBeforeData = jsonObject[this._jsdo._dataSetName]["prods:before"], - tmpIndex = {}, - record, - record2, - recordId, - key, - tmpKeyIndex, - id, - jsrecord, - tmpDataIndex, - tmpDeletedIndex, - i; - - if (prodsBeforeData && prodsBeforeData[this._name]) { - - if ((Object.keys(this._beforeImage).length !== 0) && keyFields && (keyFields.length !== 0)) { - tmpKeyIndex = {}; - for (id in this._beforeImage) { - jsrecord = this._findById(id, false); - - if (jsrecord) { - key = this._getKey(jsrecord.data, keyFields); - tmpKeyIndex[key] = jsrecord.data; - } - } - } - - for (i = 0; i < prodsBeforeData[this._name].length; i++) { - record = prodsBeforeData[this._name][i]; - tmpIndex[record["prods:id"]] = record; - - if (record["prods:rowState"] == "deleted") { - key = undefined; - - if (keyFields && (keyFields.length !== 0)) { - key = this._getKey(record, keyFields); - } - - if (tmpKeyIndex) { - if (tmpKeyIndex[key] !== undefined) { - throw new Error(msg.getMsgText("jsdoMSG032")); - } - } - - if ((tmpDataIndex === undefined) && keyFields && (keyFields.length !== 0)) { - tmpDataIndex = {}; - tmpDeletedIndex = {}; - - for (var j = 0; j < this._data.length; j++) { - record2 = this._data[j]; - if (!record2) continue; - - var key2 = this._getKey(record2, keyFields); - tmpDataIndex[key2] = record2; - } - - // We also want to check if _deleted record already exists - for (var j = 0; j < this._deleted.length; j++) { - record2 = this._deleted[j].data; - if (!record2) continue; - - var key2 = this._getKey(record2, keyFields); - tmpDeletedIndex[key2] = record2; - } - } - - // First check to see if this deleted record is already in _deleted array - if (key !== undefined) { - record2 = tmpDeletedIndex[key]; - if (record2 !== undefined) { - // If record is already in _deleted array, then nothing more to do here - continue; - } - } - - if (key !== undefined) { - record2 = tmpDataIndex[key]; - if (record2 !== undefined) { - var jsrecord = this._findById(record2._id, false); - if (jsrecord) jsrecord._remove(false); - record._id = record2._id; - } - } - - if (record._id === undefined) - record._id = progress.data._getNextId(); - var copy = {}; - this._jsdo._copyRecord( - this._tableRef, record, copy); - this._jsdo._deleteProdsProperties(copy); - this._beforeImage[record._id] = copy; - var jsrecord = new progress.data.JSRecord(this, copy); - this._deleted.push(jsrecord); - } - } - } - - // Process data using jsonObject instead of _data - // First check if there is after-data for table. Can be called with just before-image data - var tableObject = jsonObject[this._jsdo._dataSetName][this._name]; - if (tableObject) { - for (var i = 0; i < jsonObject[this._jsdo._dataSetName][this._name].length; i++) { - record = jsonObject[this._jsdo._dataSetName][this._name][i]; - recordId = undefined; - if (beforeImageJsonIndex && record["prods:id"]) { - recordId = beforeImageJsonIndex[record["prods:id"]]; - } - switch (record["prods:rowState"]) { - case "created": - if (recordId === undefined) { - recordId = record._id; - } - - // If recordId and record._id are undefined, the record was not processed - if (recordId !== undefined) { - this._beforeImage[recordId] = null; - this._added.push(recordId); - } - break; - case "modified": - var beforeRecord = tmpIndex[record["prods:id"]]; - if (beforeRecord === undefined) { - beforeRecord = {}; - } - - if (recordId === undefined) { - recordId = record._id; - } - // If recordId and record._id are undefined, the record was not processed - if (recordId !== undefined) { - beforeRecord._id = record._id; - - var copy = {}; - this._jsdo._copyRecord( - this._tableRef, beforeRecord, copy); - this._jsdo._deleteProdsProperties(copy); - - this._beforeImage[recordId] = copy; - this._changed[recordId] = record; - - this._beforeImage[beforeRecord._id] = copy; - this._changed[beforeRecord._id] = record; - } - break; - case undefined: - break; // rowState is only specified for records that have changed - default: - throw new Error(msg.getMsgText("jsdoMSG030", - "rowState value in before-image data", "'created' or 'modified'")); - } - } - } - - // Process prods:errors - var prodsErrors = jsonObject[this._jsdo._dataSetName]["prods:errors"]; - if (prodsErrors) { - for (var i = 0; i < prodsErrors[this._name].length; i++) { - var item = prodsErrors[this._name][i]; - var recordId = beforeImageJsonIndex[item["prods:id"]]; - var jsrecord = this._findById(recordId, false); - if (jsrecord) { - jsrecord.data._errorString = item["prods:error"]; - } - } - } - - tmpIndex = null; - }; - - /* - * Clears all data (including any pending changes) in buffer - */ - this._clearData = function () { - this._setRecord(null); - - // Data structure - this._data = []; - this._index = {}; - this._createIndex(); - - // Arrays to keep track of changes - this._beforeImage = {}; - this._added = []; - this._changed = {}; - this._deleted = []; - }; - - this.hasData = function () { - var data; - - // Check if we should return this table with its nested child table's data as nested - if (this._jsdo._nestChildren) { - data = this._getDataWithNestedChildren(this._data); - } - else { - data = this._getRelatedData(); - } - - if (this._hasEmptyBlocks) { - for (var i = 0; i < data.length; i++) { - var block = data[i]; - if (!block) { - return true; - } - } - } - - return data.length !== 0; - }; - - this.getData = function (params) { - var i, - data, - numEmptyBlocks, - newDataArray, - block; - - if (this._needCompaction) { - this._compact(); - } - - if (params && params.filter) { - throw new Error("Not implemented in current version"); - } - // Check if we should return this table with its nested child table's data as nested - else if (this._jsdo._nestChildren) { - data = this._getDataWithNestedChildren(this._data); - } - else { - data = this._getRelatedData(); - } - - if (this._hasEmptyBlocks) { - numEmptyBlocks = 0; - newDataArray = []; - for (i = 0; i < data.length; i += 1) { - block = data[i]; - if (block) { - newDataArray.push(block); - } - else { - numEmptyBlocks++; - } - } - if ((numEmptyBlocks * 100 / this._data.length) >= PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS) - this._needCompaction = true; - - data = newDataArray; - } - else { - // Creates a copy of the data if sort and top are specified - // so that the sorting does not happen in the JSDO memory but - // in a copy of the records - if (params && (params.sort || params.top)) { - newDataArray = []; - for (i = 0; i < data.length; i += 1) { - newDataArray.push(data[i]); - } - data = newDataArray; - } - } - - if (params && (params.sort || params.top)) { - if (params.sort) { - // Converts sort option from Kendo UI to sort option used by the JSDO - sortFields = []; - for (i = 0; i < params.sort.length; i += 1) { - field = params.sort[i].field; - if (params.sort[i].dir == "desc") { - field += ":DESC"; - } - sortFields.push(field); - } - - // Obtain sortObject from sort options to get compare functions - var sortObject = this._processSortFields(sortFields); - if (sortObject.sortFields && sortObject.sortFields.length > 0) { - sortObject.tableRef = this; - data.sort(this._getCompareFn(sortObject)); - } - } - - if (params.top) { - if (typeof(params.skip) == "undefined") { - params.skip = 0; - } - - data = data.splice(params.skip, params.top); - } - } - - return data; - }; - - this._recToDataObject = function (record, includeChildren) { - var array = [record]; - var dataObject = array; - - if (typeof(includeChildren) == 'undefined') { - includeChildren = false; - } - if (this._jsdo._dataSetName) { - dataObject = {}; - dataObject[this._jsdo._dataSetName] = {}; - dataObject[this._jsdo._dataSetName][this._name] = array; - if (includeChildren && this._children.length > 0) { - var jsrecord = this._findById(record._id, false); - if (jsrecord) { - for (var i = 0; i < this._children.length; i++) { - var tableName = this._children[i]; - dataObject[this._jsdo._dataSetName][tableName] = - this._jsdo._buffers[tableName]._getRelatedData(jsrecord); - } - } - } - } - else { - if (this._jsdo._dataProperty) { - dataObject = {}; - dataObject[this._jsdo._dataProperty] = array; - } - } - return dataObject; - }; - - this._recFromDataObject = function (dataObject) { - var data = {}; - if (dataObject) { - if (this._jsdo._dataSetName) { - if (dataObject[this._jsdo._dataSetName]) - data = dataObject[this._jsdo._dataSetName][this._name]; - } - else { - if (this._jsdo._dataProperty) { - if (dataObject[this._jsdo._dataProperty]) - data = dataObject[this._jsdo._dataProperty]; - } - else if (dataObject.data) { - data = dataObject.data; - } - else { - data = dataObject; - } - } - } - - return data instanceof Array ? data[0] : data; - }; - - // Property: schema - this.getSchema = function () { - return this._schema; - }; - this.setSchema = function (schema) { - this._schema = schema; - }; - - // Private method that returns the ABL data type for the specified field - this._getABLType = function (fieldName) { - var i, schema; - - schema = this.getSchema(); - - for (i = 0; i < schema.length; i++) { - if (schema[i].name == fieldName) { - return schema[i].ablType; - } - } - - return undefined; - }; - - // Private method that returns format property (from catalog) for the specified field - this._getFormat = function (fieldName) { - var i, schema; - - schema = this.getSchema(); - - for (i = 0; i < schema.length; i++) { - if (schema[i].name == fieldName) { - return schema[i].format; - } - } - - return undefined; - }; - - - - this.add = function (values) { - return this._add(values, true, true); - }; - - // Alias for add() method - this.create = this.add; - - this._add = function (values, trackChanges, setWorkingRecord) { - if (typeof(trackChanges) == 'undefined') { - trackChanges = true; - } - if (typeof(setWorkingRecord) == 'undefined') { - setWorkingRecord = true; - } - var record = {}, - i, - j, - value, - prefixElement, - name; - - if (typeof values === "undefined") { - values = {}; - } - - // Assign values from the schema - var schema = this.getSchema(); - for (i = 0; i < schema.length; i++) { - var fieldName = schema[i].name; - if (schema[i].type == "array") { - record[fieldName] = []; - if (schema[i].maxItems) { - for (var j = 0; j < schema[i].maxItems; j++) { - record[fieldName][j] = this._jsdo._getDefaultValue(schema[i]); - } - } - - // Assign array values from object parameter - value = values[fieldName]; - if (typeof value != "undefined") { - record[fieldName] = value; - delete values[fieldName]; - } - // Assign values from individual fields from flattened arrays - prefixElement = this._jsdo._getArrayField(fieldName); - if (!record[fieldName]) { - record[fieldName] = []; - } - for (j = 0; j < schema[i].maxItems; j += 1) { - name = prefixElement.name + (j+1); - value = values[name]; - if (typeof value != "undefined") { - if (!this._fields[name.toLowerCase()]) { - // Skip element if a field with the same name exists - // Remove property from object for element since it is not part of the actual schema - delete values[prefixElement.name + (j+1)]; - if (typeof value == 'string' && schema[i].items.type != 'string') { - value = this._jsdo._convertType(value, - schema[i].items.type, - null); - } - record[fieldName][j] = value; - } - } - } - } - else { - record[fieldName] = this._jsdo._getDefaultValue(schema[i]); - } - } - - // Assign values based on a relationship - if (this._jsdo.useRelationships && this._relationship && this._parent) { - if (this._jsdo._buffers[this._parent].record) { - for (var j = 0; j < this._relationship.length; j++) { - record[this._relationship[j].childFieldName] = - this._jsdo._buffers[this._parent].record.data[this._relationship[j].parentFieldName]; - } - } - else - throw new Error(msg.getMsgText("jsdoMSG002", this._parent)); - } - // Assign values from object parameter - for (var v in values) { - record[v] = values[v]; - } - - // Specify _id field - do not use schema default - var id; - var idProperty; - if ((idProperty = this._jsdo._resource.idProperty) !== undefined) { - id = record[idProperty]; - } - if (!id) { - id = progress.data._getNextId(); - } - else { - id += ""; - } - record._id = id; - - if (this.autoSort - && this._sortRecords - && (this._sortFn !== undefined || this._sortObject.sortFields !== undefined)) { - if (this._needsAutoSorting) { - this._data.push(record); - this._sort(); - } - else { - // Find position of new record in _data and use splice - for (var i = 0; i < this._data.length; i++) { - if (this._data[i] === null) continue; // Skip null elements - var ret = this._sortFn ? - this._sortFn(record, this._data[i]) : - this._compareFields(record, this._data[i]); - if (ret == -1) break; - } - this._data.splice(i, 0, record); - } - this._createIndex(); - } - else { - this._data.push(record); - this._index[record._id] = new progress.data.JSIndexEntry(this._data.length - 1); - } - - var jsrecord = new progress.data.JSRecord(this, record); - - // Set record property ignoring relationships - if (setWorkingRecord) - this._setRecord(jsrecord, true); - - if (trackChanges) { - // Save before image - this._beforeImage[record._id] = null; - // End - Save before image - this._added.push(record._id); - } - return jsrecord; - }; - - /* - * Returns records related to the specified jsrecord. - * If jsrecord is not specified the parent working record is used. - */ - this._getRelatedData = function (jsrecord) { - var data = []; - - if (this._data.length === 0) return data; - - if (typeof(jsrecord) == 'undefined') { - if (this._jsdo.useRelationships && this._relationship && this._parent) { - jsrecord = this._jsdo._buffers[this._parent].record; - if (!jsrecord) - throw new Error(msg.getMsgText("jsdoMSG002", this._parent)); - } - } - if (jsrecord) { - // Filter records using relationship - for (var i = 0; i < this._data.length; i++) { - var block = this._data[i]; - if (!block) continue; - - var match = false; - for (var j = 0; j < this._relationship.length; j++) { - match = (jsrecord.data[this._relationship[j].parentFieldName] == - this._data[i][this._relationship[j].childFieldName]); - if (!match) break; - } - if (match) - data.push(this._data[i]); - } - } - else - data = this._data; - - return data; - }; - - - // This method is called on a parent table that has child tables - // where the relationship is specified as NESTED. - // It returns a json array that contains the parent rows. - // If a parent row is involved in nested relationship, - // then references to the child rows are added - // to the parent row in a child table array (providing the nested format) - // We are using the internal jsdo _data arrays, - // and adding a child table array to each parent row that has children. - // Once the caller is done with the nested data, they can call jsdo._unnestData() - // which removes these child table references - this._getDataWithNestedChildren = function (data) { - - // Walk through all the rows and determine if any of its child tables - // should be associated (nested) with the current record - for (var i = 0; i < data.length; i++) { - var parentRecord = data[i]; - - // Now walk thru the parent's children to find any nested children - if (this._children && this._children.length > 0) { - for (var j = 0; j < this._children.length; j++) { - var childBuf = this._jsdo._buffers[this._children[j]]; - - if (childBuf._isNested) { - // If child is nested, then we should walk child records to find matches - for (var k = 0; k < childBuf._data.length; k++) { - var childRecord = childBuf._data[k]; - if (!childRecord) continue; - - var match = false; - for (var m = 0; m < childBuf._relationship.length; m++) { - match = (parentRecord[childBuf._relationship[m].parentFieldName] == - childRecord[childBuf._relationship[m].childFieldName]); - if (!match) break; - } - if (match) { - // Make sure that this parentRecord has an array for its child rows - if (!parentRecord[childBuf._name]) { - parentRecord[childBuf._name] = []; - } - parentRecord[childBuf._name].push(childRecord); - } - - - } // end for; finished adding all child rows for parentRecord - - // The child table may have its own nested children so call recursively - // Use child row array in current parentRecord - if (childBuf._hasNestedChild()) { - childBuf._getDataWithNestedChildren(parentRecord[childBuf._name]); - } - - - } // end if (childBuf._isNested) - } - } - - - } - return data; - - }; - - this._findFirst = function () { - if (this._jsdo.useRelationships && this._relationship && this._parent) { - if (this._jsdo._buffers[this._parent].record) { - // Filter records using relationship - for (var i = 0; i < this._data.length; i++) { - var block = this._data[i]; - if (!block) continue; - - var match = false; - var parentFieldName, childFieldName; - for (var j = 0; j < this._relationship.length; j++) { - parentFieldName = this._relationship[j].parentFieldName; - childFieldName = this._relationship[j].childFieldName; - match = (this._jsdo._buffers[this._parent].record.data[parentFieldName] == - this._data[i][childFieldName]); - if (!match) break; - } - if (match) { - return new progress.data.JSRecord(this, this._data[i]); - } - } - } - } - else { - for (var i = 0; i < this._data.length; i++) { - var block = this._data[i]; - if (!block) continue; - - return new progress.data.JSRecord(this, this._data[i]); - } - } - - - return undefined; - }; - - this._setRecord = function (jsrecord, ignoreRelationships) { - if (jsrecord) { - this.record = jsrecord; - } - else { - this.record = undefined; - } - - // Set child records only if useRelationships is true - if (this._jsdo.useRelationships) { - ignoreRelationships = ((typeof(ignoreRelationships) == 'boolean') && ignoreRelationships); - - if (this._children && this._children.length > 0) { - for (var i = 0; i < this._children.length; i++) { - var childTable = this._jsdo._buffers[this._children[i]]; - if (!ignoreRelationships && this.record && childTable._relationship) { - childTable._setRecord(childTable._findFirst()); - } - else { - childTable._setRecord(undefined, ignoreRelationships); - } - } - } - } - - if (this._jsdo._defaultTableRef) { - this._jsdo.record = this.record; - } - }; - - this.assign = function (values) { - if (this.record) { - return this.record.assign(values); - } - else - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - }; - - // Alias for assign() method - this.update = this.assign; - - this.remove = function () { - if (this.record) { - return this.record._remove(true); - } - else - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - }; - - this._remove = function (bTrackChanges) { - if (this.record) { - return this.record._remove(bTrackChanges); - } - else - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - }; - - this.getId = function () { - if (this.record) { - return this.record.data._id; - } - else - return 0; - }; - - // getErrors() - JSTableRef - this.getErrors = function () { - return this._lastErrors; - }; - - this.getErrorString = function () { - if (this.record) { - return this.record.data._errorString; - } - else - return 0; - }; - - this.findById = function (id) { - return this._findById(id, true); - }; - - this._findById = function (id, setWorkingRecord) { - if (typeof(setWorkingRecord) == 'undefined') { - setWorkingRecord = true; - } - if (id && this._index[id]) { - var record = this._data[this._index[id].index]; - this.record = record ? (new progress.data.JSRecord(this, record)) : null; - if (setWorkingRecord) - this._setRecord(this.record); - return this.record; - } - - if (setWorkingRecord) - this._setRecord(null); - return null; - }; - - /* - * Finds a record in the JSDO memory using the specified function to determine the record. - */ - this.find = function (fn) { - if (typeof(fn) != 'function') { - throw new Error(msg.getMsgText("jsdoMSG003", "find()")); - } - var data = this._getRelatedData(); - - for (var i = 0; i < data.length; i++) { - var block = data[i]; - if (!block) { - continue; - } - this._setRecord(new progress.data.JSRecord(this, data[i])); - var result = fn(this.record); - if (typeof(result) != 'boolean') { - throw new Error(msg.getMsgText("jsdoMSG007", "find()")); - } - if (result) { - return this.record; - } - } - - this._setRecord(null); - return null; - }; - - /* - * Loops through the records - */ - this.foreach = function (fn) { - if (typeof(fn) != 'function') { - throw new Error(msg.getMsgText("jsdoMSG003", "foreach()")); - } - var numEmptyBlocks = 0; - if (this._needCompaction) - this._compact(); - - var data = this._getRelatedData(); - - this._inforeach = true; - for (var i = 0; i < data.length; i++) { - var block = data[i]; - if (!block) { - numEmptyBlocks++; - continue; - } - - this._setRecord(new progress.data.JSRecord(this, data[i])); - var result = fn(this.record); - if ((typeof(result) != 'undefined') && !result) - break; - } - - this._inforeach = false; - - if ((numEmptyBlocks * 100 / this._data.length) >= PROGRESS_JSDO_PCT_MAX_EMPTY_BLOCKS) - this._needCompaction = true; - }; - - this._equalRecord = function (rec1, rec2, keyFields) { - var field; - var match = true; - for (var i = 0; i < keyFields.length; i++) { - var fieldName = keyFields[i]; - var value1 = rec1[fieldName]; - var value2 = rec2[fieldName]; - - if (!jsdo[tableName].caseSensitive) { - field = jsdo[tableName]._fields[fieldName.toLowerCase()]; - if (field && field.type == "string") { - if (value1 !== undefined && value1 !== null) - value1 = value1.toUpperCase(); - if (value2 !== undefined && value2 !== null) - value2 = value2.toUpperCase(); - } - } - - match = (value1 == value2); - if (!match) return false; - } - return true; - }; - - // Private method to merge changes using merge modes: APPEND, EMPTY, MERGE and REPLACE - this._getKey = function (record, keyFields) { - var keyObject = {}; - for (var i = 0; i < keyFields.length; i++) { - var fieldName = keyFields[i]; - var value = record[fieldName]; - - if (!jsdo[tableName].caseSensitive) { - field = jsdo[tableName]._fields[fieldName.toLowerCase()]; - if (field && field.type == "string") { - if (value !== undefined && value !== null) - value = value.toUpperCase(); - } - } - keyObject[fieldName] = value; - } - return JSON.stringify(keyObject); - }; - - this._getCompareFn = function (sortObject) { - if (typeof sortObject == 'function') { - return function (rec1, rec2) { - if (rec1 === null) return 1; - if (rec2 === null) return -1; - - var jsrec1 = new progress.data.JSRecord(this, rec1); - var jsrec2 = new progress.data.JSRecord(this, rec2); - return sortObject(jsrec1, jsrec2); - }; - } - else return function (rec1, rec2) { - var tableRef = sortObject.tableRef; - var sortFields = sortObject.sortFields; - if (!(sortFields instanceof Array)) return 0; - var sortAscending = sortObject.sortAscending; - - if (rec1 === null) return 1; - if (rec2 === null) return -1; - - var field; - for (var i = 0; i < sortFields.length; i++) { - var fieldName = sortFields[i]; - var value1 = rec1[fieldName]; - var value2 = rec2[fieldName]; - - if (!tableRef.caseSensitive) { - field = tableRef._fields[fieldName.toLowerCase()]; - if (field && field.type == "string") { - if (value1 !== undefined && value1 !== null) - value1 = value1.toUpperCase(); - if (value2 !== undefined && value2 !== null) - value2 = value2.toUpperCase(); - } - } - if (value1 > value2 || (value1 === undefined || value1 === null)) - return sortAscending[i] ? 1 : -1; - else if (value1 < value2 || (value2 === undefined && value2 === null)) - return sortAscending[i] ? -1 : 1; - } - return 0; - }; - }; - - this._sortObject = {}; - this._sortObject.tableRef = this; - this._sortObject.sortFields = undefined; - this._sortObject.sortAscending = undefined; - this._compareFields = this._getCompareFn(this._sortObject); - - // _sortRecords - Tells the table reference whether to sort on add, assign and addRecords - this._sortRecords = true; - // Tells the table reference whether an autoSort is required on an add or assign - this._needsAutoSorting = false; - this._sortFn = undefined; - if ((typeof Object.defineProperty) == 'function') { - this._autoSort = true; - Object.defineProperty( - this, - "autoSort", - { - get: function () { - return this._autoSort; - }, - set: function (value) { - if (value) { - this._autoSort = true; - if (this._sortFn || this._sortObject.sortFields) { - this._sort(); - this._createIndex(); - } - } - else - this._autoSort = false; - }, - enumerable: true, - writeable: true - }); - this._caseSensitive = false; - Object.defineProperty( - this, - "caseSensitive", - { - get: function () { - return this._caseSensitive; - }, - set: function (value) { - if (value) { - this._caseSensitive = true; - } - else - this._caseSensitive = false; - if (this.autoSort && - (this._sortObject.sortFields && !this._sortFn)) { - this._sort(); - this._createIndex(); - } - }, - enumerable: true, - writeable: true - }); - } - else { - this.autoSort = true; - this.caseSensitive = false; // caseSensitive is false by default - } - - this._processSortFields = function (sortFields) { - var sortObject = {}; - if (sortFields instanceof Array) { - sortObject.sortFields = sortFields; - sortObject.sortAscending = []; - sortObject.fields = {}; - for (var i = 0; i < sortObject.sortFields.length; i++) { - var idx; - var fieldName; - var field; - - if (typeof (sortObject.sortFields[i]) != 'string') { - throw new Error(msg.getMsgText("jsdoMSG030", "sort field name", "string element")); - } - if ((idx = sortObject.sortFields[i].indexOf(':')) != -1) { - fieldName = sortObject.sortFields[i].substring(0, idx); - var sortOrder = sortObject.sortFields[i].substring(idx + 1); - switch (sortOrder.toUpperCase()) { - case 'ASCENDING': - case 'ASC': - sortObject.sortAscending[i] = true; - break; - case 'DESCENDING': - case 'DESC': - sortObject.sortAscending[i] = false; - break; - default: - throw new Error(msg.getMsgText("jsdoMSG030", - "sort order '" + sortObject.sortFields[i].substring(idx + 1) + "'", - "ASCENDING or DESCENDING")); - } - } - else { - fieldName = sortObject.sortFields[i]; - sortObject.sortAscending[i] = true; - } - if (fieldName != "_id" && this._fields) { - field = this._fields[fieldName.toLowerCase()]; - if (field) { - if (field.type == "array") - throw new Error(msg.getMsgText("jsdoMSG030", "data type found in sort", - "scalar field")); - fieldName = field.name; - } - else - throw new Error(msg.getMsgText("jsdoMSG031", fieldName)); - } - sortObject.sortFields[i] = fieldName; - sortObject.fields[fieldName] = fieldName; - } - } - else { - sortObject.sortFields = undefined; - sortObject.sortAscending = undefined; - sortObject.fields = undefined; - } - return sortObject; - }; - - this.setSortFields = function (sortFields) { - if (sortFields === undefined || sortFields === null) { - this._sortObject.sortFields = undefined; - this._sortObject.sortAscending = undefined; - } - else if (sortFields instanceof Array) { - var sortObject = this._processSortFields(sortFields); - this._sortObject.sortFields = sortObject.sortFields; - this._sortObject.sortAscending = sortObject.sortAscending; - this._sortObject.fields = sortObject.fields; - - if (this.autoSort) { - this._sort(); - this._createIndex(); - } - } - else - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "setSortFields()")); - }; - - this.setSortFn = function (fn) { - // Check that fn parameter is a function - // Valid values are a function, undefined, or null - // Documentation mentions null as a way to clear the sort function - if (fn && typeof (fn) != 'function') { - throw new Error(msg.getMsgText("jsdoMSG030", "parameter in setSortFn()", - "function parameter")); - } - this._sortFn = fn ? this._getCompareFn(fn) : undefined; - if (this.autoSort) { - this._sort(); - this._createIndex(); - } - }; - - this.sort = function (arg1) { - if (arg1 === undefined || arg1 === null) { - throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "sort()")); - } - if (arguments.length !== 1 || - (!(arg1 instanceof Array) && typeof(arg1) != 'function')) { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "sort()")); - } - - if (arg1 instanceof Array) { - var sortObject = this._processSortFields(arg1); - if (sortObject.sortFields && sortObject.sortFields.length > 0) - this._sort(sortObject); - } - else { - this._sort(arg1); - } - this._createIndex(); - }; - - this._sort = function (arg1) { - if (arguments.length === 0 && - (!this.autoSort || (this._sortFn === undefined && this._sortObject.sortFields === undefined))) - return; - - if (arguments.length === 0) { - if (this._sortFn) { - // Sort using function - this._data.sort(this._sortFn); - } - else { - // Sort using sort fields - this._data.sort(this._compareFields); - } - this._needsAutoSorting = false; - } - else { - if (typeof(arg1) == 'function') { - // Sort using function - this._data.sort(this._getCompareFn(arg1)); - } - else { - // Sort using sort fields - arg1.tableRef = this; - this._data.sort(this._getCompareFn(arg1)); - } - if (this.autoSort) - this._needsAutoSorting = true; - } - }; - - /* - * Reads a JSON object into the JSDO memory for the specified table reference. - */ - this.addRecords = function (jsonObject, addMode, keyFields, trackChanges, isInvoke) { - this._jsdo._addRecords(this._name, jsonObject, addMode, keyFields, trackChanges, isInvoke); - }; - - /* - * Accepts changes for the specified table reference. - */ - this.acceptChanges = function () { - var tableRef = this; - - // First, let's remove any "prods:" properties from created and updated records. - // Don't have to worry about deleted records, since they're going away. - for (var id in tableRef._beforeImage) { - // Create - if (tableRef._beforeImage[id] === null) { - var jsrecord = tableRef._findById(id, false); - if (jsrecord !== null) { - tableRef._jsdo._deleteProdsProperties(jsrecord.data, true); - } - - } - // Update - else if (this._changed[id] !== undefined) { - var jsrecord = this._findById(id, false); - if (jsrecord !== null) { - tableRef._jsdo._deleteProdsProperties(jsrecord.data, true); - } - } - } - - tableRef._processed = {}; - tableRef._added = []; - tableRef._changed = {}; - tableRef._deleted = []; - tableRef._beforeImage = {}; - }; - - /* - * Rejects changes for the specified table reference. - */ - this.rejectChanges = function () { - // Reject changes - for (var id in this._beforeImage) { - if (this._beforeImage[id] === null) { - // Undo create - this._jsdo._undoCreate(this, id); - } - else if (this._changed[id] !== undefined) { - // Undo update - this._jsdo._undoUpdate(this, id, true); - } - else { - // Undo delete - this._jsdo._undoDelete(this, id, true); - } - } - - var tableRef = this; - tableRef._processed = {}; - tableRef._added = []; - tableRef._changed = {}; - tableRef._deleted = []; - }; - - this.hasChanges = function () { - return (Object.keys(this._beforeImage).length !== 0); - }; - - this.getChanges = function () { - var result = []; - for (var id in this._beforeImage) { - var item = {rowState: "", record: null}; - // Create - if (this._beforeImage[id] === null) { - item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_CREATE]; - item.record = this._findById(id, false); - } - // Update - else if (this._changed[id] !== undefined) { - item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_UPDATE]; - item.record = this._findById(id, false); - } - // Delete - else { - item.rowState = PROGRESS_JSDO_ROW_STATE_STRING[progress.data.JSDO._OP_DELETE]; - item.record = new progress.data.JSRecord(this, this._beforeImage[id]); - } - result.push(item); - } - return result; - }; - - /* - * Private method to apply changes for the specified table reference. - * If _errorString has been set for a row, row change is rejected. - * If it has not been set, acceptRowChanges() is called. - */ - this._applyChanges = function () { - for (var id in this._beforeImage) { - // Create - if (this._beforeImage[id] === null) { - var jsrecord = this._findById(id, false); - if (jsrecord !== null) { - if (jsrecord.data._rejected - || (jsrecord.data._errorString !== undefined)) { - this._jsdo._undoCreate(this, id); - } - else { - jsrecord.acceptRowChanges(); - } - } - else { - // Record not present in JSDO memory - // Delete after Create - var found = false; - for (var i = 0; i < this._deleted.length; i++) { - found = (this._deleted[i].data._id == id); - if (found) break; - } - if (!found) { - throw new Error(msg.getMsgText("jsdoMSG000", - "Created record appears to be deleted without a delete operation.")); - } - } - } - // Update - else if (this._changed[id] !== undefined) { - var jsrecord = this._findById(id, false); - if (jsrecord !== null) { - // Record found in JSDO memory - if (jsrecord.data._rejected - || (jsrecord.data._errorString !== undefined)) { - this._jsdo._undoUpdate(this, id); - } - else { - jsrecord.acceptRowChanges(); - } - } - else { - // Record not present in JSDO memory - // Delete after Update - if (this._beforeImage[id]._rejected - || (this._beforeImage[id]._errorString !== undefined)) { - this._jsdo._undoDelete(this, id); - } - else { - var found = false; - for (var i = 0; i < this._deleted.length; i++) { - found = (this._deleted[i].data._id == id); - if (found) break; - } - if (!found) { - throw new Error(msg.getMsgText("jsdoMSG000", - "Updated record appears to be deleted without a delete operation.")); - } - } - } - } - // Delete - else { - if (this._beforeImage[id]._rejected - || (this._beforeImage[id]._errorString !== undefined)) { - this._jsdo._undoDelete(this, id); - } - } - } - - var tableRef = this; - tableRef._processed = {}; - tableRef._added = []; - tableRef._changed = {}; - tableRef._deleted = []; - tableRef._beforeImage = {}; - }; - - - /* - * Accepts row changes for the working record at the table reference level. - */ - this.acceptRowChanges = function () { - if (this.record) - return this.record.acceptRowChanges(); - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - }; - - /* - * Rejects row changes for the working record at the table reference level. - */ - this.rejectRowChanges = function () { - if (this.record) - return this.record.rejectRowChanges(); - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - }; - - - /* This method returns true - * if this table has any child tables and at least one of those tables is nested. - * Else if returns false. - */ - this._hasNestedChild = function () { - var hasNestedChild = false; - var childBufObj; - - // If table has children, see if any relationship is NESTED - if (this._children.length > 0) { - for (var i = 0; i < this._children.length; i++) { - childBufObj = this._jsdo._buffers[this._children[i]]; - - if (childBufObj._isNested) { - hasNestedChild = true; - break; - } - } - } - - return hasNestedChild; - }; - }; - - /* - * Returns a JSRecord for the specified JSDO. - * @param jsdo the JSDO - * @param record the values of the record - */ - progress.data.JSRecord = function JSRecord(tableRef, record) { - this._tableRef = tableRef; - this.data = record; - - this.getId = function () { - return this.data._id ? this.data._id : null; - }; - - this.getErrorString = function () { - return this.data._errorString; - }; - - /* - * Saves a copy of the current record to the before image. - */ - this._saveBeforeImageUpdate = function () { - // Save before image - if (this._tableRef._beforeImage[this.data._id] === undefined) { - // this.data._index = index; - var copy = {}; - this._tableRef._jsdo._copyRecord( - this._tableRef, this.data, copy); - this._tableRef._beforeImage[this.data._id] = copy; - } - - if (this._tableRef._changed[this.data._id] === undefined) { - this._tableRef._changed[this.data._id] = this.data; - } - // End - Save before image - }; - - /* - * - */ - this._sortRecord = function (fields) { - var index = this._tableRef._index[this.data._id].index; - var record = this._tableRef._data[index]; - - if (this._tableRef.autoSort - && this._tableRef._sortRecords - && (this._tableRef._sortFn !== undefined - || this._tableRef._sortObject.sortFields !== undefined)) { - - if (this._tableRef._sortObject.fields) { - if (typeof fields == 'string') { - if (this._tableRef._sortObject.fields[fields] === undefined) - return; // Only sort records if the the specified field is in the sort fields - } - else if (fields instanceof Array) { - var found = false; - for (var i = 0; i < fields.length; i++) { - if (this._tableRef._sortObject.fields[fields[i]] !== undefined) { - found = true; - break; - } - } - if (!found) - return; // Only sort records if the the specified fields are in the sort fields - } - } - - if (this._tableRef._needsAutoSorting) { - this._tableRef._sort(); - this._tableRef._createIndex(); - } - else { - // Find position of new record in _data and use splice - for (var i = 0; i < this._tableRef._data.length; i++) { - if (this._tableRef._data[i] === null) continue; // Skip null elements - if (i == index) continue; // Skip changed record - var ret = this._tableRef._sortFn ? - this._tableRef._sortFn(record, this._tableRef._data[i]) : - this._tableRef._compareFields(record, this._tableRef._data[i]); - if (ret == -1) break; - } - - if (i > index) { - i--; - } - if (i != index) { - this._tableRef._data.splice(index, 1); - this._tableRef._data.splice(i, 0, record); - this._tableRef._createIndex(); - } - } - } - }; - - /* - * Assigns the specified values. - * @param record parameter with the record values - */ - this.assign = function (record) { - if (record === undefined) - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "assign() or update()")); - - this._saveBeforeImageUpdate(); - - var fieldName, - i, - j, - value, - schema = this._tableRef.getSchema(), - prefixElement, - name; - - if (record) { - for (i = 0; i < schema.length; i += 1) { - fieldName = schema[i].name; - value = record[fieldName]; - if (typeof value != "undefined") { - if (typeof value == 'string' && schema[i].type != 'string') { - value = this._tableRef._jsdo._convertType(value, - schema[i].type, - schema[i].items ? schema[i].items.type : null); - } - this.data[fieldName] = value; - } - if (schema[i].type === "array") { - // Assign values from individual fields from flattened arrays - prefixElement = this._tableRef._jsdo._getArrayField(fieldName); - if (!this.data[fieldName]) { - this.data[fieldName] = []; - } - for (j = 0; j < schema[i].maxItems; j += 1) { - name = prefixElement.name + (j+1); - value = record[name]; - if (typeof value != "undefined") { - // Skip element if a field with the same name exists - if (!this._tableRef._fields[name.toLowerCase()]) { - if (typeof value == 'string' && schema[i].items.type != 'string') { - value = this._tableRef._jsdo._convertType(value, - schema[i].items.type, - null); - } - this.data[fieldName][j] = value; - } - } - } - } - } - - this._sortRecord(); - } - return true; - }; - - // Alias for assign() method - this.update = this.assign; - - /* - * Removes the JSRecord. - */ - this.remove = function () { - return this._remove(true); - }; - - this._remove = function (bTrackChanges) { - if (typeof(bTrackChanges) == 'undefined') { - bTrackChanges = true; - } - - var index = this._tableRef._index[this.data._id].index; - var jsrecord = this._tableRef._findById(this.data._id, false); - - if (bTrackChanges) { - // Save before image - var record = this._tableRef._beforeImage[this.data._id]; - if (record === undefined) { - // Record does not exist in the before image - this.data._index = index; - this._tableRef._beforeImage[this.data._id] = this.data; - } - else { - // Record exists in the before image - if (record) { - // Record is not null - a null entry in the before image indicates - // corresponds to an add - // Save the index of the record - // so that an undo would restore the record in the same position in _data - record._index = index; - } - } - // End - Save before image - this._tableRef._deleted.push(jsrecord); - } - - // Set entry to null instead of removing entry - index requires positions to be persistent - this._tableRef._data[index] = null; - this._tableRef._hasEmptyBlocks = true; - delete this._tableRef._index[this.data._id]; - - // Set record property - this._tableRef._setRecord(null); - - return true; - }; - - /* - * Accepts row changes for the specified record. - */ - this.acceptRowChanges = function () { - var id = this.data._id; - if (this._tableRef._beforeImage[id] !== undefined) { - if (this.data._rejected) { - throw new Error(msg.getMsgText("jsdoMSG127")); - } - if (this._tableRef._beforeImage[id] === null) { - // Accept create - // Remove element from _added - for (var i = 0; i < this._tableRef._added.length; i++) { - if (this._tableRef._added[i] == id) { - this._tableRef._added.splice(i, 1); - break; - } - } - this._tableRef._jsdo._deleteProdsProperties(this.data, true); - } - else if (this._tableRef._changed[id] !== undefined) { - // Accept update - delete this._tableRef._changed[id]; - this._tableRef._jsdo._deleteProdsProperties(this.data, true); - } - else { - // Accept delete - // Remove element from _deleted - for (var i = 0; i < this._tableRef._deleted.length; i++) { - if (this._tableRef._deleted[i].data._id == id) { - this._tableRef._deleted.splice(i, 1); - break; - } - } - } - delete tableRef._beforeImage[id]; - } - }; - - /* - * Rejects row changes for the specified record. - */ - this.rejectRowChanges = function () { - var id = this.data._id; - if (this._tableRef._beforeImage[id] !== undefined) { - if (this._tableRef._beforeImage[id] === null) { - // Undo create - this._tableRef._jsdo._undoCreate(this._tableRef, id); - // Remove element from _added - for (var i = 0; i < this._tableRef._added.length; i++) { - if (this._tableRef._added[i] == id) { - this._tableRef._added.splice(i, 1); - break; - } - } - } - else if (this._tableRef._changed[id] !== undefined) { - // Undo update - this._tableRef._jsdo._undoUpdate(this._tableRef, id, true); - delete this._tableRef._changed[id]; - } - else { - // Undo delete - this._tableRef._jsdo._undoDelete(this._tableRef, id, true); - // Remove element from _deleted - for (var i = 0; i < this._tableRef._deleted.length; i++) { - if (this._tableRef._deleted[i].data._id == id) { - this._tableRef._deleted.splice(i, 1); - break; - } - } - } - delete tableRef._beforeImage[id]; - } - }; - - }; - - /* - * Returns a JSDO for the specified resource. - * @param resNameOrParmObj: the resource name or an object that contains the initial values for the JSDO - * (if this is an object, it should include the name property with the resource name - * @param serviceName : name of service (ignored if 1st param is an object containing the initial values) - */ - progress.data.JSDO = function JSDO(resNameOrParmObj, serviceName) { - var _super = {}; - - if (typeof progress.data.Session == 'undefined') { - throw new Error('ERROR: You must include progress.session.js'); - } - - _super.subscribe = this.subscribe; - - // Override for Observable.subscribe - this.subscribe = function(evt) { - var args = Array.prototype.slice.call(arguments); - if (typeof evt === "string") { - // Aliases for events - switch(evt.toLowerCase()) { - case "beforeread": - args[0] = "beforefill"; - break; - case "afterread": - args[0] = "afterfill"; - break; - } - } - _super.subscribe.apply(this, args); - }; - - this._defineProperty = function (tableName, fieldName) { - Object.defineProperty( - this._buffers[tableName], - fieldName, - { - get: function fnGet() { - var name, - index, - element, - fieldInfo; - if (this.record) { - index = fieldName.indexOf(progress.data.JSDO.ARRAY_INDEX_SEPARATOR); - if (index > 0 && !this._fields[fieldName.toLowerCase()]) { - // Skip element if a field with the same name exists - // Check if field is a flattened array field by quickly checking for the separator - // Extract name and index element - name = fieldName.substring(0, index); - element = fieldName.substring(index + progress.data.JSDO.ARRAY_INDEX_SEPARATOR.length); - fieldInfo = this._fields[name.toLowerCase()]; - if (!isNaN(element) && fieldInfo && (fieldInfo.type === "array")) { - return this.record.data[name][element - 1]; - } - } - return this.record.data[fieldName]; - } - else - return null; - }, - set: function (value) { - var name = fieldName, - index, - element, - fieldInfo; - if (this.record) { - this.record._saveBeforeImageUpdate(); - - try { - index = fieldName.indexOf(progress.data.JSDO.ARRAY_INDEX_SEPARATOR); - if (index > 0 && !this._fields[fieldName.toLowerCase()]) { - // Skip element if a field with the same name exists - name = fieldName.substring(0, index); - element = fieldName.substring(index + progress.data.JSDO.ARRAY_INDEX_SEPARATOR.length); - fieldInfo = this._fields[name.toLowerCase()]; - if (!isNaN(element) && fieldInfo && (fieldInfo.type === "array")) { - this.record.data[name][element - 1] = value; - return; - } - } - this.record.data[fieldName] = value; - } - finally { - this.record._sortRecord(name); - } - } - }, - enumerable: true, - writeable: true - }); - }; - - // Initial values - this._buffers = {}; // Object of table references - this._numBuffers = 0; - this._defaultTableRef = null; - - this._async = true; - this._dataProperty = null; - this._dataSetName = null; - this.operations = []; - this.useRelationships = true; - - this._session = null; - this._needCompaction = false; - - this._hasCUDOperations = false; - this._hasSubmitOperation = false; - this._useSubmit = false; // For saving saveChanges(useSubmit) param - - this.autoApplyChanges = true; // default should be true to support 11.2 behavior - this._lastErrors = []; - this._localStorage = null; - this._convertForServer; - var autoFill = false; - - // Initialize JSDO using init values - if (!arguments[0]) { - throw new Error("JSDO: Parameters are required in constructor."); - } - - if (typeof(arguments[0]) == "string") { - this.name = arguments[0]; -// if ( arguments[1] && (typeof(arguments[1]) == "string") ) -// localServiceName = serviceName; - } - else if (typeof(arguments[0]) == "object") { - var args = arguments[0]; - for (var v in args) { - switch (v) { - case 'autoFill': - autoFill = args[v]; - break; - case 'events': - this._events = {}; - for (var eventName in args[v]) { - this._events[eventName.toLowerCase()] = args[v][eventName]; - } - break; - case 'dataProperty': - this._dataProperty = args[v]; - break; - default: - this[v] = args[v]; - } - } - } - /* error out if caller didn't pass the resource name */ - if ((!this.name) /*|| !(this._session)*/) { - // make this error message more specific? - throw new Error("JSDO: JSDO constructor is missing the value for 'name'"); - } - - /* perform some basic validation on the event object for the proper structure if provided */ - if (this._events) { - if ((typeof this._events) !== 'object') { - throw new Error("JSDO: JSDO constructor event object is not defined as an object"); - } - - /* make sure all the event handlers are sane */ - for (var prop in this._events) { - var evt = this._events[prop]; - if (!(evt instanceof Array)) { - throw new Error('JSDO: JSDO constructor event object for ' + prop + ' must be an array'); - } - evt.forEach(function (el) { - if ((typeof el) !== 'object') { - throw new Error("JSDO: JSDO constuctor event object for " + - prop + " is not defined as an object"); - } - /* listener must have at least fn property defined as a function */ - if ((typeof el.fn) !== 'function') { - throw new Error("JSDO: JSDO event listener for " + prop + " is not a function."); - } - /* scope is optional, but must be an object if provided */ - if (el.scope && (typeof el.scope) !== 'object') { - throw new Error("JSDO: JSDO event listener scope for " + prop + " is not an object."); - } - }); - } - } - - if (this.name) { - // Read resource definition from the Catalog - save reference to JSDO - // Enhance this to deal with multiple services loaded and the same resource - // name is used by more than one service (use the local serviceName var) - this._resource = progress.data.ServicesManager.getResource(this.name); - if (this._resource) { - if (!this.url) - this.url = this._resource.url; - if (!this._dataSetName && this._resource._dataSetName) { - // Catalog defines a DataSet - this._dataSetName = this._resource._dataSetName; - - // Define TableRef property in the JSDO - if (this._resource.dataProperty) { - var buffer = this[this._resource.dataProperty] - = new progress.data.JSTableRef(this, this._resource.dataProperty); - this._buffers[this._resource.dataProperty] = buffer; - } - else { - for (var tableName in this._resource.fields) { - var buffer = this[tableName] - = new progress.data.JSTableRef(this, tableName); - this._buffers[tableName] = buffer; - } - } - } - if (!this._dataProperty && this._resource.dataProperty) - this._dataProperty = this._resource.dataProperty; - - if (!this._dataSetName) { - var tableName = this._dataProperty ? this._dataProperty : ""; - this._buffers[tableName] = new progress.data.JSTableRef(this, tableName); - if (tableName) - this[tableName] = this._buffers[tableName]; - } - - // Add functions for operations to JSDO object - for (var fnName in this._resource.fn) { - this[fnName] = this._resource.fn[fnName]["function"]; - } - // Check if CUD operations have been defined - this._hasCUDOperations = - this._resource.generic["create"] !== undefined - || this._resource.generic["update"] !== undefined - || this._resource.generic["delete"] !== undefined; - this._hasSubmitOperation = this._resource.generic["submit"] !== undefined; - - /* get a session object, using name of the service to look it up in the list of - * sessions maintained by the ServicesManager - */ - if (!this._session) { - var myservice = progress.data.ServicesManager.getService(this._resource.service.name); - this._session = myservice._session; - this._session._pushJSDOs(this); - } - } - else { - throw new Error(msg.getMsgText("jsdoMSG004", this.name)); - } - } - else { - this._buffers[""] = new progress.data.JSTableRef(this, ""); - } - - if (!this._session) { - throw new Error("JSDO: Unable to get user session for resource '" + this.name + "'"); - } - - // Calculate _numBuffers and _defaultTableRef - for (var buf in this._buffers) { - this._buffers[buf]._parent = null; - this._buffers[buf]._children = []; - // The _relationship object is only specified for the child buffer. - // Currently it is limited to only a single relationship. ie. It does not support the - // where the child buffer is involved in more than one data-relation - this._buffers[buf]._relationship = null; - this._buffers[buf]._isNested = false; - if (!this._defaultTableRef) - this._defaultTableRef = this._buffers[buf]; - this._numBuffers++; - } - if (this._numBuffers != 1) - this._defaultTableRef = null; - else { - // record is used to represent the current record for a table reference - // data corresponds to the values (JSON object) of the data - this.record = null; - } - - // Define caseSensitive property at the JSDO level - if ((typeof Object.defineProperty) == 'function') { - this._caseSensitive = false; // caseSensitive is false by default - Object.defineProperty( - this, - "caseSensitive", - { - get: function () { - return this._caseSensitive; - }, - set: function (value) { - this._caseSensitive = value ? true : false; - - for (var buf in this._buffers) { - this._buffers[buf].caseSensitive = this._caseSensitive; - } - }, - enumerable: true, - writeable: true - }); - this._autoSort = true; // autoSort is true by default - Object.defineProperty( - this, - "autoSort", - { - get: function () { - return this._autoSort; - }, - set: function (value) { - this._autoSort = value ? true : false; - - for (var buf in this._buffers) { - this._buffers[buf].autoSort = this._autoSort; - } - }, - enumerable: true, - writeable: true - }); - } - - // Define _properties property at the JSDO level - this._properties = {}; - if ((typeof Object.defineProperty) == 'function') { - Object.defineProperty( this, - "this._properties", - { - get: function () { - return this._properties; - }, - enumerable: false - } - ); - - } - - - // Set schema for TableRef - if (this._resource && this._resource.fields) { - for (var buf in this._buffers) { - this._buffers[buf]._schema = this._resource.fields[buf]; - this._buffers[buf]._primaryKeys = this._resource.primaryKeys[buf]; - - // Create _fields object used to validate fields as case-insensitive. - this._buffers[buf]._fields = {}; - var fields = this._buffers[buf]._schema; - for (var i = 0; i < fields.length; i++) { - this._buffers[buf]._fields[fields[i].name.toLowerCase()] = fields[i]; - if (typeof(fields[i].origName) !== "undefined") { - if ((typeof(fields[i].origName) !== "string") - || (fields[i].origName.trim() === "")) { - throw new Error(msg.getMsgText("jsdoMSG504", - "JSDO", "Field '" + fields[i].name + "' in resource '" + this._resource.name + "'", "origName")); - } - } - } - - if (this._buffers[buf]._schema && (typeof Object.defineProperty) == 'function') { - // Add fields as properties of the TableRef object - for (var i = 0; i < this._buffers[buf]._schema.length; i++) { - var fieldName = this._buffers[buf]._schema[i].name, - fieldInfo = this._buffers[buf]._schema[i]; - if (typeof(this._buffers[buf][fieldName]) == 'undefined') { - this._defineProperty(buf, fieldName); - } - if (fieldInfo.type === "array") { - for (var j = 0; j < fieldInfo.maxItems; j += 1) { - var name = fieldName + progress.data.JSDO.ARRAY_INDEX_SEPARATOR + (j + 1); - // Skip element if a field with the same name exists - // Only create property if the name is not being used - if (!this._buffers[buf]._fields[name.toLowerCase()]) { - this._defineProperty(buf, name); - } - } - } - } - } - } - // Set schema for when dataProperty is used but not specified via the catalog - if (this._defaultTableRef - && !this._defaultTableRef._schema - && this._resource.fields[""]) { - this._defaultTableRef._schema = this._resource.fields[""]; - } - } - else { - if (this._defaultTableRef) - this._defaultTableRef._schema = []; - } - - // Set isNested property - if (this._numBuffers > 1) { - for (var buf in this._buffers) { - var fields = []; - var found = false; - for (var i = 0; i < this._buffers[buf]._schema.length; i++) { - var field = this._buffers[buf]._schema[i]; - - if (field.items - && field.type == "array" && field.items.$ref) { - if (this._buffers[field.name]) { - found = true; - this._buffers[field.name]._isNested = true; - } - } - else - fields.push(field); - } - // Replace list of fields - removing nested datasets from schema - if (found) - this._buffers[buf]._schema = fields; - } - } - - // Process relationships - if (this._resource && this._resource.relations) { - for (var i = 0; i < this._resource.relations.length; i++) { - var relationship = this._resource.relations[i]; - - // Set relationship information ignoring self-referencing (recursive) relationships - if (relationship.childName - && relationship.parentName - && (relationship.childName !== relationship.parentName)) { - // Set casing of fields in relationFields to be the same as in the schema - if (relationship.relationFields instanceof Array) { - for (var j = 0; j < relationship.relationFields.length; j++) { - var fieldName; - var field; - if (this._buffers[relationship.parentName]._fields) { - fieldName = relationship.relationFields[j].parentFieldName; - field=this._buffers[relationship.parentName]._fields[fieldName.toLowerCase()]; - if (field) { - relationship.relationFields[j].parentFieldName = field.name; - } - else - throw new Error(msg.getMsgText("jsdoMSG010", fieldName)); - } - if (this._buffers[relationship.childName]._fields) { - fieldName = relationship.relationFields[j].childFieldName; - field=this._buffers[relationship.childName]._fields[fieldName.toLowerCase()]; - if (field) { - relationship.relationFields[j].childFieldName = field.name; - } - else - throw new Error(msg.getMsgText("jsdoMSG010", fieldName)); - } - } - } - this._buffers[relationship.childName]._parent = relationship.parentName; - this._buffers[relationship.childName]._relationship = relationship.relationFields; - this._buffers[relationship.parentName]._children.push(relationship.childName); - } - } - } - - this._getDefaultValue = function (field) { - var defaultValue, - t, m, d, - isDate = false; - - if ((field.type === "string") - && field.format - && (field.format.indexOf("date") !== -1) - && (field["default"])) { - isDate = true; - } else if ((field.type === "array") - && field.ablType - && (field.ablType.indexOf("DATE") != -1) - && (field["default"])) { - isDate = true; - } else { - defaultValue = field["default"]; - } - - if (isDate) { - switch (field["default"].toUpperCase()) { - case "NOW": - defaultValue = new Date().toISOString(); - break; - case "TODAY": - t = new Date(); - m = String((t.getMonth() + 1)); - if (m.length === 1) { - m = '0' + m; - } - d = String((t.getDate())); - if (d.length === 1) { - d = '0' + d; - } - defaultValue = t.getFullYear() + '-' + m + '-' + d; - break; - default: - defaultValue = field["default"]; - } - } - - return defaultValue; - }; - - // Method to calculate the element information of an array given the name, index, and value - // Parameters: - // arrayFieldName The name o the field - // index Optional parameter - if index is null/undefined the name of the element is the prefix - // value Optional parameter - this._getArrayField = function (arrayFieldName, index, value) { - var element = {}; - // ABL arrays are 1-based - element.name = arrayFieldName + progress.data.JSDO.ARRAY_INDEX_SEPARATOR + ((index >= 0) ? (index + 1) : ""); - element.value = value ? value[index] : undefined; - return element; - }; - - this.isDataSet = function () { - return this._dataSetName ? true : false; - }; - - /* handler for invoke operation complete */ - this._invokeComplete = function (jsdo, success, request) { - // only fire on async requests - if (request.async && request.fnName) { - jsdo.trigger('afterInvoke', request.fnName, jsdo, success, request); - } - - if (request.deferred) { - if (success) { - request.deferred.resolve(jsdo, success, request); - } - else { - request.deferred.reject(jsdo, success, request); - } - } - }; - - /* handler for invoke operation success */ - this._invokeSuccess = function (/* jsdo, success, request */) { - // do nothing - }; - - /* handler for invoke operation error */ - this._invokeError = function (/* jsdo, success, request */) { - // do nothing - }; - - /* - * Performs an HTTP request using the specified parameters. This is - * used to perform remote calls for the JSDO for operations defined. - * - */ - this._httpRequest = function (xhr, method, url, reqBody, request) { - - function afterOpenRequest() { - var input = null; - if (reqBody) { - xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); - input = JSON.stringify(reqBody); - } - - try { - xhr.send(input); - } catch (e) { - request.success = false; - request.exception = e; - // let Session check for online/offline - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - } - } - - // if xhr wasn't passed we'll create our own since this is an invoke operation - // if xhr is passed, then it is probably a CRUD operation which is setup with XHR - // in call to session - if (!xhr) { - xhr = new XMLHttpRequest(); - - // only setup the callback handlers if we're responsible for creating the - // xhr call which happens on invoke operations...which is the normal case - // the CRUD operations setup their own callbacks and they have their own - // event handlers so we don't use them here. - xhr.onCompleteFn = this._invokeComplete; - xhr.onSuccessFn = this._invokeSuccess; - xhr.onErrorFn = this._invokeError; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - - // for invokes we always fire the invoke when doing async - if (request.async && request.fnName) { - this.trigger('beforeInvoke', request.fnName, this, request); - } - - // For Invoke operations, wrap reqBody in a request object - // This is not required for CRUD operations since the whole - // reqBody is mapped to the parameter - if (reqBody) { - if (this._resource && this._resource.service) { - var useRequest = this._resource.service.useRequest; - if (this._resource.service.settings - && this._resource.service.settings.useRequest !== undefined) { - useRequest = this._resource.service.settings.useRequest; - } - if (useRequest) { - reqBody = {request: reqBody}; - } - } - } - } - - xhr.request = request; - xhr.jsdo = this; - request.jsdo = this; - request.xhr = xhr; - - this._session._openRequest(xhr, method, url, request.async, afterOpenRequest); - - return request; // Note: for the async case, this does not give us exactly the same behavior - // as when afterOpenRequest is called synchronously, because this returns - // request before its xhr has had its open() called - }; - - - // This method currently is just used by the JSDOReadService. - // It returns data in its non-nested (default) format - this._getDataObject = function () { - var dataObject = {}; - if (this._dataSetName) { - dataObject[this._dataSetName] = {}; - - var oldUseRelationships = this.useRelationships; - // Turn off useRelationships so that getData() returns all the records - try { - this.useRelationships = false; - for (var buf in this._buffers) { - dataObject[this._dataSetName][buf] = this._buffers[buf].getData(); - } - } - finally { - // Restore useRelationships - this.useRelationships = oldUseRelationships; - } - } - else { - if (this._dataProperty) { - dataObject[this._dataProperty] = this.getData(); - } - else - return this.getData(); // Array - } - return dataObject; - }; - - - // This method currently is just used by the JSDOReadService. - // Now that the JSDO Services support nested data, we want to return data nested for those - // relationships that are marked nested. - // - // This method returns a data object containing the nested data. - // If a parent row is involved in nested relationship, - // then references to its child rows are added to the parent row in a child table array - // (providing the nested format). - // We are using the internal jsdo _data arrays, - // and adding a child table array to each parent row that has children. - // Once the caller is done with the nested data, - // they can call jsdo._unnestData() which removes these child table references - // - this._getDataObjectAsNested = function () { - var dataObject = {}; - if (this._dataSetName) { - dataObject[this._dataSetName] = {}; - - try { - // First walk thru all buffers. We need to determine if any of the buffers are - // involved in a nested relationship. If so, we want to return the child's - // data in nested format. - for (var buf in this._buffers) { - var bufObj = this._buffers[buf]; - - - // If this is a child table, and its involved in a nested relationship, - // then just skip. - // This table's data will be nested within each parent row when we - // process the parent table. - if (bufObj._isNested) continue; - - this._nestChildren = false; // default to false - - // If table has children, see if any relationship is NESTED - if (bufObj._children.length > 0) { - for (var i = 0; i < bufObj._children.length; i++) { - var childBufObj = this._buffers[bufObj._children[i]]; - - if (childBufObj._isNested) { - this._nestChildren = true; - break; - } - } - } - - dataObject[this._dataSetName][buf] = this._buffers[buf].getData(); - } - } - catch (e) { - throw new Error(msg.getMsgText("jsdoMSG000", e.message)); - } - finally { - // Set back to default avlue - this._nestChildren = false; - } - } - else { - if (this._dataProperty) { - dataObject[this._dataProperty] = this.getData(); - } - else - return this.getData(); // Array - } - return dataObject; - }; - - - // This method is used in conjunction with _getDataObjectAsNested() in the JSDOReadService. - // _getDataObjectAsNested() adds arrays of child row references to their parent rows. - // Once the JSDOReadService has done its data mapping, we need to remove the references since - // internally the JSDO stores its data in unnested format. - this._unnestData = function () { - - if (this._dataSetName) { - var parentRecord; - var bufObj; - var childBufObj; - - // First walk thru all buffers. We need to determine if any of the buffers are parent - // buffers involved in a nested relationship. If so, then we'll look for any child row arrays - // to delete - for (var buf in this._buffers) { - bufObj = this._buffers[buf]; - - // If we know this table has at least one nested child table, we'll walk thru - // all its rows to determine if the rows have any child row arrays. - // It's more efficient to just walk thru the parent row list once, so we'll - // check for all child row arrays here - - if (bufObj._hasNestedChild()) { - // Now must walk thru the parent rows and delete any child row arrays - for (var i = 0; i < bufObj._data.length; i++) { - parentRecord = bufObj._data[i]; - - for (var j = 0; j < bufObj._children.length; j++) { - childBufObj = this._buffers[bufObj._children[j]]; - - if (parentRecord[childBufObj._name]) { - delete parentRecord[childBufObj._name]; - } - } - - } - } - } // end for - } - }; - - - this._recToDataObject = function (record, includeChildren) { - if (this._defaultTableRef) - return this._defaultTableRef._recToDataObject(record, includeChildren); - throw new Error(msg.getMsgText("jsdoMSG001", "_recToDataObject()")); - }; - - this._recFromDataObject = function (dataObject) { - if (this._defaultTableRef) - return this._defaultTableRef._recFromDataObject(dataObject); - throw new Error(msg.getMsgText("jsdoMSG001", "_recFromDataObject()")); - }; - - this.add = function (obj) { - if (this._defaultTableRef) - return this._defaultTableRef.add(obj); - throw new Error(msg.getMsgText("jsdoMSG001", "add() or create()")); - }; - - // Alias for add() method - this.create = this.add; - - this.hasData = function () { - for (var buf in this._buffers) { - if (this._buffers[this._buffers[buf]._name].hasData()) - return true; - } - return false; - }; - - this.getData = function (params) { - if (this._defaultTableRef) - return this._defaultTableRef.getData(params); - throw new Error(msg.getMsgText("jsdoMSG001", "getData()")); - }; - - this.getSchema = function () { - if (this._defaultTableRef) - return this._defaultTableRef.getSchema(); - throw new Error(msg.getMsgText("jsdoMSG001", "getSchema()")); - }; - - this.findById = function (id) { - if (this._defaultTableRef) - return this._defaultTableRef.findById(id); - throw new Error(msg.getMsgText("jsdoMSG001", "findById()")); - }; - - this._convertType = function (value, type, itemType) { - if ((typeof value != 'string') || (type === null)) return value; - var result = value; - try { - if (type == 'array') { - var result = []; - - value = value.slice(1, value.length - 1); - var elements = value.split(','); - var convertItem = (itemType && (itemType != 'string')); - for (var i = 0; i < elements.length; i++) { - result[i] = convertItem ? this._convertType(elements[i], itemType, null) : elements[i]; - } - } - else if (type == 'integer') { - result = parseInt(value); - } - else if (type == 'number') { - result = parseFloat(value); - } - else { - result = value; - } - } - catch (e) { - throw new Error(msg.getMsgText("jsdoMSG000", - "Error converting string to native type: " + e.message)); - } - return result; - }; - - this.assign = function (values) { - if (this._defaultTableRef) { - return this._defaultTableRef.assign(values); - } - else - throw new Error(msg.getMsgText("jsdoMSG001", "assign() or update()")); - }; - - // Alias for assign() method - this.update = this.assign; - - this.remove = function () { - if (this._defaultTableRef) { - return this._defaultTableRef.remove(); - } - else - throw new Error(msg.getMsgText("jsdoMSG001", "remove()")); - }; - - this.getId = function () { - if (this._defaultTableRef) - return this._defaultTableRef.getId(); - throw new Error(msg.getMsgText("jsdoMSG001", "getId()")); - }; - - // getErrors() - JSDO - this.getErrors = function () { - if (this._defaultTableRef) - return this._defaultTableRef.getErrors(); - throw new Error(msg.getMsgText("jsdoMSG001", "getErrors()")); - }; - - this.getErrorString = function () { - if (this._defaultTableRef) - return this._defaultTableRef.getErrorString(); - throw new Error(msg.getMsgText("jsdoMSG001", "getErrorString()")); - }; - - /* - * Finds a record in the JSDO memory using the specified function to determine the record. - */ - this.find = function (fn) { - if (this._defaultTableRef) - return this._defaultTableRef.find(fn); - throw new Error(msg.getMsgText("jsdoMSG001", "find()")); - }; - - this.foreach = function (fn) { - if (this._defaultTableRef) - return this._defaultTableRef.foreach(fn); - throw new Error(msg.getMsgText("jsdoMSG001", "foreach()")); - }; - - this.setSortFields = function (sortFields) { - if (this._defaultTableRef) - return this._defaultTableRef.setSortFields(sortFields); - throw new Error(msg.getMsgText("jsdoMSG001", "setSortFields()")); - }; - - this.setSortFn = function (fn) { - if (this._defaultTableRef) - return this._defaultTableRef.setSortFn(fn); - throw new Error(msg.getMsgText("jsdoMSG001", "setSortFn()")); - }; - - this.sort = function (arg1) { - if (this._defaultTableRef) - return this._defaultTableRef.sort(arg1); - throw new Error(msg.getMsgText("jsdoMSG001", "sort()")); - }; - - this._clearErrors = function () { - this._lastErrors = []; - for (var buf in this._buffers) { - this._buffers[buf]._lastErrors = []; - } - }; - - /** - * setAllRecordsRejected - * - * Sets _allRecordsRejected flag to indicate whether all records have been rejected - * in a saveChanges() call. - * If changes are specified as an array, the changes are used to calculate the flag. - * - * @param {*} param - Array with changes or boolean with value - */ - this._setAllRecordsRejected = function (param) { - var changes, - hasErrors, - hasRejected, - hasCommittedRecords, - i; - - // Note: This function is a single one-stop convenient function to set - // _allRecordsRejected and _someRecordsRejected. - // This logic can be optimized by setting the flags while processing the response. - if (param instanceof Object) { - if (param instanceof Array) { - changes = param; - hasErrors = false; - - this._allRecordsRejected = false; - this._someRecordsRejected = false; - - for (var buf in this._buffers) { - if (this._buffers[buf]._lastErrors.length > 0) { - hasErrors = true; - } - } - if (hasErrors) { - this._allRecordsRejected = true; - this._someRecordsRejected = true; - - for (i = 0; i < changes.length; i += 1) { - if (changes[i].record && !changes[i].record.data._rejected) { - this._allRecordsRejected = false; - return; - } - } - } else if (changes.length > 0) { - this._allRecordsRejected = true; - this._someRecordsRejected = false; - hasCommittedRecords = false; - - for (i = 0; i < changes.length; i += 1) { - if (changes[i].record) { - if (changes[i].record.data._rejected) { - this._someRecordsRejected = true; - } else { - hasCommittedRecords = true; - } - } - } - if (hasCommittedRecords && !this._someRecordsRejected) { - this._allRecordsRejected = false; - } - } - } else { - if (param.operations instanceof Array) { - if (param.operations.length > 0 - && !param.operations[0].success) { - // First operation failed - this._allRecordsRejected = true; - this._someRecordsRejected = true; - - for (i = 0; i < param.operations.length; i += 1) { - if (param.operations[i].success) { - this._allRecordsRejected = false; - return; - } - } - } else { - // Not all operations were rejected - this._allRecordsRejected = false; - this._someRecordsRejected = false; - - for (i = 0; i < param.operations.length; i += 1) { - if (!param.operations[i].success) { - this._someRecordsRejected = true; - return; - } - } - } - } - } - } else { - // Possible values: true, false, undefined - this._allRecordsRejected = param; - this._someRecordsRejected = param; - } - }; - - /* - * Loads data from the HTTP resource. - */ - this.fill = function () { - var objParam, - promise, - properties, - mapping; - - // Clear errors before sending request - this._clearErrors(); - - // Reset _allRecordsRejected - this._setAllRecordsRejected(undefined); - - // Process parameters - if (arguments.length !== 0) { - // Call to fill() has parameters - if (typeof(arguments[0]) == 'function') { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", "fill() or read()")); - } - - properties = this.getMethodProperties("read"); - - // Get plugin if mappingType is not undefined, null, or "" - if (properties && properties.mappingType) { - mapping = progress.data.PluginManager.getPlugin(properties.mappingType); - if (!mapping) { - throw new Error(msg.getMsgText("jsdoMSG118", properties.mappingType)); - } - } - - // fill( string); - var filter; - if (arguments[0] === null || arguments[0] === undefined) { - filter = ""; - } - else if (typeof(arguments[0]) == "string") { - filter = arguments[0]; - objParam = {filter: filter}; - } - else if (typeof(arguments[0]) == "object") { - // options - // ablFilter, id, top, skip, sort - - // Use plugin if mappingType is not undefined, null, or "" - if (mapping) { - if (typeof(mapping.requestMapping) === "function") { - objParam = mapping.requestMapping(this, arguments[0], { operation: "read" }); - } - else { - objParam = arguments[0]; - } - } - else { - if (properties.capabilities) { - throw new Error(msg.getMsgText("jsdoMSG119")); - } - objParam = arguments[0]; - } - } - else { - throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "fill() or read()")); - } - } - else { - // fill(); - objParam = null; - } - - var xhr = new XMLHttpRequest(); - var request = { - xhr: xhr, - jsdo: this, - objParam: objParam - }; - - xhr.request = request; - xhr.jsdo = this; - - xhr.onSuccessFn = this._fillSuccess; - xhr.onErrorFn = this._fillError; - xhr.onCompleteFn = this._fillComplete; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - - this.trigger("beforeFill", this, request); - - if (this._resource) { - if (typeof(this._resource.generic.read) == "function") { - xhr.objParam = objParam; - this._resource.generic.read.call(this, xhr, this._async); - if (xhr.request.deferred) { - promise = xhr.request.deferred.promise(); - } - } - else { - throw new Error("JSDO: READ operation is not defined."); - } - } - else { - // Old approach to call READ - this._session._openRequest(xhr, 'GET', this.url, this._async); - try { - xhr.send(null); - } - catch (e) { - request.exception = e; - // get the Client Context ID (AppServer ID) - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - } - } - - // This is the scenario where the read.call did not reach server. i.e., - // some problem in between making successful call to server and we are - // completing the fill() operation with necessary cleanup operations - if (request.success == false && request.exception) { - if ((typeof xhr.onErrorFn) == 'function') { - xhr.onErrorFn(xhr.jsdo, request.success, request); - } - - // get the Client Context ID (AppServer ID) - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - - if ((typeof xhr.onCompleteFn) == 'function') { - xhr.onCompleteFn(xhr.jsdo, request.success, request); - } - } - - return promise; - }; - - // Alias for fill() method - this.read = this.fill; - - /* - * Clears all data (including any pending changes) for each buffer in JSDO - */ - this._clearData = function () { - for (var buf in this._buffers) { - this._buffers[buf]._clearData(); - } - }; - - /* - * Executes a CRUD operation using the built-in API. - */ - this._execGenericOperation = function (operation, objParam, request, - onCompleteFn, onSuccessFn, onErrorFn) { - - var xhr = new XMLHttpRequest(); - request.xhr = xhr; - request.jsdo = this; - request.objParam = objParam; - request.operation = operation; - xhr.jsdo = this; - xhr.onCompleteFn = onCompleteFn; - xhr.onSuccessFn = onSuccessFn; - xhr.onErrorFn = onErrorFn; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - xhr.request = request; - - this._convertRequestData(objParam); - - var operationStr; - switch (operation) { - case progress.data.JSDO._OP_READ: - case progress.data.JSDO._OP_CREATE: - case progress.data.JSDO._OP_UPDATE: - case progress.data.JSDO._OP_DELETE: - case progress.data.JSDO._OP_SUBMIT: - operationStr = PROGRESS_JSDO_OP_STRING[operation]; - break; - default: - throw new Error("JSDO: Unexpected operation " + operation + " in HTTP request."); - } - - if (this._resource) { - if (typeof(this._resource.generic[operationStr]) == "function") { - xhr.objParam = objParam; - this._resource.generic[operationStr](xhr, this._async); - } - else { - // "JSDO: {1} operation is not defined." - throw new Error(msg.getMsgText("jsdoMSG046", operationStr.toUpperCase() )); - } - } - }; - - // Determines if any fields need a conversion when data sent to backend - this._initConvertForServer = function () { - var i, buf, schema; - - // If set, we're good. Field lists for conversion have already been created - if (this._convertForServer !== undefined) { - return; - } - - this._convertForServer = false; - for (buf in this._buffers) { - schema = this._buffers[buf].getSchema(); - this._buffers[buf]._convertFieldsForServer = []; - this._buffers[buf]._convertForServer = false; - - // Check if any fields need conversion - for (i = 0; i < schema.length; i++) { - if (schema[i].ablType && this._ablTypeNeedsConversion(schema[i].ablType)) { - this._buffers[buf]._convertFieldsForServer.push({name: schema[i].name, - ablType: schema[i].ablType}); - } - } - if (this._buffers[buf]._convertFieldsForServer.length > 0) { - this._convertForServer = true; - this._buffers[buf]._convertForServer = true; - } - } - }; - - this._convertRequestData = function (objParam) { - var buf, - beforeData; - - if (this._convertForServer === false) { - return; - } - - // We know at least one table has a field to convert - for (buf in this._buffers) { - if (this._buffers[buf]._convertForServer) { - if (objParam[this._dataSetName]) { - // First convert after-table - if (objParam[this._dataSetName][buf]) { - this._convertTableData(this._buffers[buf], objParam[this._dataSetName][buf]); - } - - // Now let's convert before-image data - beforeData = objParam[this._dataSetName]["prods:before"]; - if (beforeData && beforeData[buf]) { - this._convertTableData(this._buffers[buf], beforeData[buf]); - } - } - // This is for case where saveChanges(false) is called with no before-image data - else if (objParam[buf]) { - this._convertTableData(this._buffers[buf], objParam[buf]); - } - } - } - }; - - this._convertTableData = function (tableRef, tableData) { - var i; - - for (i = 0; i < tableData.length; i++) { - this._convertRowData(tableRef, tableData[i]); - } - }; - - this._convertRowData = function (tableRef, record) { - var i, - field; - - for (i = 0; i < tableRef._convertFieldsForServer.length; i += 1) { - field = tableRef._convertFieldsForServer[i]; - record[field.name] = this._convertField(record[field.name], field.ablType); - } - }; - - this._convertField = function (value, ablType) { - var result; - - if (value === undefined || value === null) { - return value; - } - - if (value instanceof Array) { - var resultArray = []; - for (var i = 0; i < value.length; i++) { - resultArray[i] = this._convertField(value[i], ablType); - } - return resultArray; - } - - try { - switch (ablType.toUpperCase()) { - case "DATE": - case "DATETIME": - if (typeof value === 'string') { - result = value; - } - else if (value instanceof Date) { - result = this._convertDate(value, ablType.toUpperCase()); - } - else { - throw new Error("Unexpected value for " + ablType.toUpperCase() + "."); - } - break; - default: - result = value; - break; - } - } - catch (e) { - throw new Error(msg.getMsgText("jsdoMSG000", - "Error in _convertField for value: " + value + ". " + e.message)); - } - - return result; - }; - - // Convert Date object to string for DATE and DATETIME ablTypes - // Not necessary to do for DATETIME-TZ since JSON.stringify() will do correct conversion - this._convertDate = function (value, ablType) { - var result = value; - - // DATE format should be in ISO 8601 format yyyy-mm-dd - // DATETIME format should be in ISO 8601 format yyyy-mm-ddThh:mm:ss.sss - if (ablType === "DATE" || ablType === "DATETIME") { - result = progress.util._pad(value.getFullYear(), 4) + '-' + - progress.util._pad(value.getMonth() + 1) + '-' + - progress.util._pad(value.getDate()); - - if (ablType === "DATETIME") { - result = result + "T" + - progress.util._pad(value.getHours()) + ":" + - progress.util._pad(value.getMinutes()) + ":" + - progress.util._pad(value.getSeconds()) + "." + - progress.util._pad(value.getMilliseconds(), 3); - } - } - - return result; - }; - - - this._ablTypeNeedsConversion = function (ablType) { - - var needsConversion = false; - - switch (ablType.toUpperCase()) { - case "DATE": - case "DATETIME": - needsConversion = true; - break; - } - - return needsConversion; - }; - - - - this._undefWorkingRecord = function () { - // Set record property - for (var buf in this._buffers) { - this._buffers[buf]._setRecord(null); - } - }; - - /* - * Saves changes in the JSDO. Save any outstanding changes for CREATES, UPDATE, and DELETEs - */ - this.saveChanges = function (useSubmit) { - var promise, - request; - - if (useSubmit === undefined) { - useSubmit = false; - } - else if (typeof(useSubmit) != 'boolean') { - throw new Error(msg.getMsgText("jsdoMSG025", "JSDO", "saveChanges()")); - } - - // _fireCUDTriggersForSubmit() needs to know how saveChanges() was called - this._useSubmit = useSubmit; - - // confirm the availability of the operations required for executing this saveChanges call - // (_checkThatJSDOHasRequiredOperations() throws an error if there's a missing operation, - // which this method deliberately allows to bubble up to the caller) - this._checkThatJSDOHasRequiredOperations(); - - // Don't allow Submit with just a temp-table if autoApplyChanges is true - if ( !this._dataSetName && this._useSubmit && this.autoApplyChanges) { - /* error message: "autoApplyChanges is not supported for submit with a temp-table */ - /* Use jsdo.autoApplyChanges = false." */ - throw new Error(msg.getMsgText("jsdoMSG124")); - } - - // Check if any data being sent to server needs to first be converted - this._initConvertForServer(); - - // Clear errors before sending request - this._clearErrors(); - - // Reset _allRecordsRejected - this._setAllRecordsRejected(undefined); - - request = { - jsdo: this - }; - - this.trigger("beforeSaveChanges", this, request); - - if (useSubmit) { - /* Pass in request object. - * Need to use same request object so before and after saveChanges events - * are in sync in JSDO Submit Service. */ - promise = this._syncDataSetForSubmit(request); - } - else if (this._dataSetName) { - promise = this._syncDataSetForCUD(); - } - else { - promise = this._syncSingleTable(); - } - - return promise; - }; - - /* - * _checkThatJSDOHasRequiredOperations - - This method is intended to be used by the saveChanges() method to determine whether - the JSDO's resource definition includes the operations necessary for executing the - types of changes that are pending in the JSDO. It checks for Submit if saveChanges - was called with useSubmit set to true, otherwise it checks whatever CUD operations are - pending. - The JSDO's internal _useSubmit property must be set correctly before this method - is called - */ - this._checkThatJSDOHasRequiredOperations = function( ) { - var checkedDelete = false, - checkedCreate = false, - checkedUpdate = false, - buf, - tableRef; - - if (!this._hasCUDOperations && !this._hasSubmitOperation) { - throw new Error(msg.getMsgText("jsdoMSG026")); - } - - // Validate the use of Submit - if (this._useSubmit) { - if (!this._hasSubmitOperation) { - // "JSDO: {1} operation is not defined."; - throw new Error(msg.getMsgText("jsdoMSG046", "SUBMIT")); - } - else { - return; - } - } - - if (!this._resource) { - // Need the _resource property to do the validation. If not present, just return - // and let execution run as normal (presumably there will be an error) - return; - } - - // Find the pending operations and make sure they are defined - for (buf in this._buffers) { - - tableRef = this._buffers[buf]; - - if (!checkedDelete && tableRef._deleted.length > 0) { - this._confirmOperationExists( progress.data.JSDO._OP_DELETE ); - checkedDelete = true; - } - - if (!checkedCreate && tableRef._added.length > 0) { - this._confirmOperationExists( progress.data.JSDO._OP_CREATE ); - checkedCreate = true; - } - - if (!checkedUpdate && Object.keys(tableRef._changed).length > 0) { - this._confirmOperationExists( progress.data.JSDO._OP_UPDATE ); - checkedUpdate = true; - } - - if ( checkedDelete && checkedCreate && checkedUpdate ) { - break; - } - } - - }; - - // Determines whether a given operation is defined by the JSDO's resource - // throws an error if it's not defined - this._confirmOperationExists = function(operation) { - var operationStr = PROGRESS_JSDO_OP_STRING[operation]; - if (typeof(this._resource.generic[operationStr]) !== "function") { - // "JSDO: {1} operation is not defined." - throw new Error(msg.getMsgText("jsdoMSG046", operationStr.toUpperCase() )); - } - }; - - this.invoke = function (name, object) { - var request = this[name](object); - if (request.deferred) { - return request.deferred.promise(); - } - - return undefined; - }; - - /* - * Synchronizes changes for a TableRef - * - * @param operation HTTP operation to be performed - * @param tableRef Handle to the TableRef - * @param batch Optional. batch information associated with the sync operation. - * If not specified a new one will be created. Used for saving datasets. - */ - this._syncTableRef = function (operation, tableRef, batch) { - var rowData, - requestData, - jsonObject; - - if (tableRef._visited) return; - tableRef._visited = true; - - //ensure batch object is sane - if (!batch) { - batch = { - operations: [] - }; - } else if (!batch.operations) { - batch.operations = []; - } - - // Before children - // Create parent records before children - switch (operation) { - case progress.data.JSDO._OP_CREATE: - for (var i = 0; i < tableRef._added.length; i++) { - var id = tableRef._added[i]; - var jsrecord = tableRef._findById(id, false); - - if (!jsrecord) continue; - if (tableRef._processed[id]) continue; - tableRef._processed[id] = jsrecord.data; - - rowData = {}; - jsonObject = {}; - - // Make copy of row data, in case we need to convert data for backend.. - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - - if (this.isDataSet()) { - if (this._useBeforeImage("create")) { - jsonObject[this._dataSetName] = {}; - var dataSetObject = jsonObject[this._dataSetName]; - dataSetObject["prods:hasChanges"] = true; - - dataSetObject[tableRef._name] = []; - - // Dont need to send prods:id for create, - // no before table or error table to match - // Dont need to send prods:clientId - since only sending one record - rowData["prods:rowState"] = "created"; - rowData["prods:clientId"] = jsrecord.data._id; - - delete rowData["_id"]; - - dataSetObject[tableRef._name].push(rowData); - } - else { - jsonObject[tableRef._name] = []; - jsonObject[tableRef._name].push(rowData); - } - } - else { - jsonObject = rowData; - } - - - var request = { - operation: operation, - batch: batch, - jsrecord: jsrecord, - jsdo: this - }; - batch.operations.push(request); - - jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); - this.trigger("beforeCreate", this, jsrecord, request); - - this._execGenericOperation( - progress.data.JSDO._OP_CREATE, jsonObject, request, this._createComplete, - this._createSuccess, this._createError); - } - break; - case progress.data.JSDO._OP_UPDATE: - for (var id in tableRef._changed) { - var jsrecord = tableRef._findById(id, false); - - if (!jsrecord) continue; - if (tableRef._processed[id]) continue; - tableRef._processed[id] = jsrecord.data; - - rowData = {}; - jsonObject = {}; - requestData = {}; - - // Make copy of row data, in case we need to convert data for backend.. - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - - var useBeforeImageFormat = false; - if (this.isDataSet()) { - if (this._useBeforeImage("update")) { - useBeforeImageFormat = true; - jsonObject[this._dataSetName] = {}; - var dataSetObject = jsonObject[this._dataSetName]; - dataSetObject["prods:hasChanges"] = true; - dataSetObject[tableRef._name] = []; - - // Dont need to send prods:clientId - since only sending one record - rowData["prods:id"] = jsrecord.data._id; - rowData["prods:rowState"] = "modified"; - rowData["prods:clientId"] = jsrecord.data._id; - delete rowData["_id"]; - - dataSetObject[tableRef._name].push(rowData); - - // Now create before-table data - dataSetObject["prods:before"] = {}; - var beforeObject = dataSetObject["prods:before"]; - beforeObject[tableRef._name] = []; - - var beforeRowData = {}; - // Dont need to send prods:clientId - since only sending one record - beforeRowData["prods:id"] = jsrecord.data._id; - - tableRef._jsdo._copyRecord(tableRef, - tableRef._beforeImage[jsrecord.data._id], beforeRowData); - delete beforeRowData["_id"]; - - beforeObject[tableRef._name].push(beforeRowData); - } - } - - if (!useBeforeImageFormat) { - if (this._resource.service - && this._resource.service.settings - && this._resource.service.settings.sendOnlyChanges) { - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData, - tableRef._beforeImage[jsrecord.data._id]); - - if (this._resource.idProperty) { - requestData[this._resource.idProperty] = - jsrecord.data[this._resource.idProperty]; - } - else { - throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, - " for sendOnlyChanges property")); - } - } - else - requestData = rowData; - - if (this.isDataSet()) { - jsonObject[tableRef._name] = []; - jsonObject[tableRef._name].push(requestData); - } - else { - jsonObject = rowData; - } - } - - var request = { - jsrecord: jsrecord, - operation: operation, - batch: batch, - jsdo: this - }; - batch.operations.push(request); - - jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); - this.trigger("beforeUpdate", this, jsrecord, request); - - this._execGenericOperation( - progress.data.JSDO._OP_UPDATE, jsonObject, request, this._updateComplete, - this._updateSuccess, this._updateError); - } - break; - } - - // Call _syncTableRef on child tables - for (var i = 0; i < tableRef._children.length; i++) { - var childTableName = tableRef._children[i]; - this._syncTableRef( - operation, this._buffers[childTableName], batch); - } - - // After children - // Delete parent records after children - - if (operation == progress.data.JSDO._OP_DELETE) { - for (var i = 0; i < tableRef._deleted.length; i++) { - var id = tableRef._deleted[i]._id; - var jsrecord = tableRef._deleted[i]; - - if (!jsrecord) continue; - tableRef._processed[id] = jsrecord.data; - - rowData = {}; - jsonObject = {}; - requestData = {}; - - // Make copy of row data, in case we need to convert data for backend.. - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - - var useBeforeImageFormat = false; - if (this.isDataSet()) { - if (this._useBeforeImage("delete")) { - useBeforeImageFormat = true; - jsonObject[this._dataSetName] = {}; - var dataSetObject = jsonObject[this._dataSetName]; - dataSetObject["prods:hasChanges"] = true; - - // There is no after tables for deletes, so just create before-table data - dataSetObject["prods:before"] = {}; - var beforeObject = dataSetObject["prods:before"]; - beforeObject[tableRef._name] = []; - - var beforeRowData = {}; - - // Dont need to send prods:id for delete, no after table or error table to match - // Dont need to send prods:clientId - since only sending one record - beforeRowData["prods:rowState"] = "deleted"; - beforeRowData["prods:clientId"] = jsrecord.data._id; - - tableRef._jsdo._copyRecord(tableRef, - tableRef._beforeImage[rowData._id], beforeRowData); - beforeObject[tableRef._name].push(beforeRowData); - } - } - - if (!useBeforeImageFormat) { - if (this._resource.service - && this._resource.service.settings - && this._resource.service.settings.sendOnlyChanges) { - if (this._resource.idProperty) { - requestData[this._resource.idProperty] = - jsrecord.data[this._resource.idProperty]; - } - else { - throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, - " for sendOnlyChanges property")); - } - } - else { - requestData = rowData; - } - - if (this.isDataSet()) { - jsonObject[tableRef._name] = []; - jsonObject[tableRef._name].push(requestData); - } - else { - jsonObject = rowData; - } - } - - var request = { - batch: batch, - jsrecord: jsrecord, - operation: operation, - jsdo: this - }; - - batch.operations.push(request); - - jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); - this.trigger("beforeDelete", this, jsrecord, request); - - this._execGenericOperation( - progress.data.JSDO._OP_DELETE, jsonObject, request, this._deleteComplete, - this._deleteSuccess, this._deleteError); - } - } - }; - - /* - * Returns true if the specified operation type was specified in the catalog as useBeforeImage, - * else it returns false. - */ - this._useBeforeImage = function (opType) { - - for (var idx = 0; idx < this._resource.operations.length; idx++) { - if (this._resource.operations[idx].type == opType) { - return this._resource.operations[idx].useBeforeImage; - } - } - - return false; - }; - - - /* - * Synchronizes changes for a DataSet. This is called when we send over one row at at time - * to Create, Update and Delete methods. - * It handles row with or without before-image data. - */ - this._syncDataSetForCUD = function () { - var batch = { - operations: [] - }, - deferred, - promise; - - if (typeof($) == 'function' && typeof($.Deferred) == 'function') { - deferred = $.Deferred(); - promise = deferred.promise(); - batch.deferred = deferred; - } - - // Process buffers - // Synchronize deletes - for (var buf in this._buffers) { - this._buffers[buf]._visited = false; - } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._syncTableRef( - progress.data.JSDO._OP_DELETE, tableRef, batch); - } - - // Synchronize adds - for (var buf in this._buffers) { - this._buffers[buf]._visited = false; - } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._syncTableRef( - progress.data.JSDO._OP_CREATE, tableRef, batch); - } - - // Synchronize updates - for (var buf in this._buffers) { - this._buffers[buf]._visited = false; - } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._syncTableRef( - progress.data.JSDO._OP_UPDATE, tableRef, batch); - } - - if (this.autoApplyChanges) { - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - tableRef._processed = {}; - tableRef._added = []; - tableRef._changed = {}; - tableRef._deleted = []; - } - } - - // OE00229270 If _async is false, this ensures that afterSaveChanges() is called just once - // We now do this after all operations have been processed - if (!this._async) { - if (this._isBatchComplete(batch)) { - var success = this._isBatchSuccess(batch); - var request = { - batch: batch, - success: success - }; - this._undefWorkingRecord(); - - // Save error messages - this._lastErrors = []; - if (!success && batch.operations) { - this._updateLastErrors(this, batch, null); - } - this._setAllRecordsRejected(batch); - - this._fireAfterSaveChanges(success, request); - } - } - // end OE00229270 - - return promise; - }; - - - /* - * Synchronizes changes for a single table - */ - this._syncSingleTable = function () { - var deferred, promise; - if (!this._defaultTableRef) return; - var tableRef = this._defaultTableRef; - - var batch = { - operations: [] - }; - - if (typeof($) == 'function' && typeof($.Deferred) == 'function') { - deferred = $.Deferred(); - promise = deferred.promise(); - batch.deferred = deferred; - } - - var fireAfterSaveChanges = false; - - // Skip delete for records that were added - // mark them as processed - var addedRecords = {}; - for (var i = 0; i < tableRef._added.length; i++) { - var id = tableRef._added[i]; - addedRecords[id] = id; - } - for (var i = 0; i < tableRef._deleted.length; i++) { - var jsrecord = tableRef._deleted[i]; - if (!jsrecord) continue; - - var id = jsrecord.data._id; - if (addedRecords[id]) { - // Set request object - // Properties async, fnName, objParam, and response - // are not set when the HTTP request is suppressed - var request = { - success: true, - xhr: undefined, - operation: progress.data.JSDO._OP_DELETE, - batch: batch, - jsrecord: jsrecord, - jsdo: this - }; - batch.operations.push(request); - tableRef._processed[id] = jsrecord.data; - - var jsdo = request.jsdo; - try { - request.jsrecord._tableRef.trigger("afterDelete", jsdo, request.jsrecord, - request.success, request); - jsdo.trigger("afterDelete", jsdo, request.jsrecord, request.success, request); - } finally { - request.complete = true; - } - - fireAfterSaveChanges = true; - } - } - addedRecords = null; - - // Synchronize deletes - for (var i = 0; i < tableRef._deleted.length; i++) { - var jsrecord = tableRef._deleted[i]; - if (!jsrecord) continue; - - var id = jsrecord.data._id; - if (tableRef._processed[id]) continue; - - tableRef._processed[id] = jsrecord.data; - fireAfterSaveChanges = false; - - var xhr = new XMLHttpRequest(); - xhr.jsdo = this; - - var request = { - xhr: xhr, - operation: progress.data.JSDO._OP_DELETE, - batch: batch, - jsrecord: jsrecord, - jsdo: this - }; - batch.operations.push(request); - xhr.onCompleteFn = this._deleteComplete; - xhr.onSuccessFn = this._deleteSuccess; - xhr.onErrorFn = this._deleteError; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - xhr.request = request; - - jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); - this.trigger("beforeDelete", this, jsrecord, request); - - var requestData = {}; - if (this._resource.service - && this._resource.service.settings - && this._resource.service.settings.sendOnlyChanges) { - if (this._resource.idProperty) { - requestData[this._resource.idProperty] = jsrecord.data[this._resource.idProperty]; - } - else { - throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, - " for sendOnlyChanges property")); - } - } - else { - // We must copy record in case _convertRowData() needs to make conversion - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData); - } - - if (tableRef._convertForServer) { - this._convertRowData(tableRef, requestData); - } - - if (this._resource) { - if (typeof(this._resource.generic["delete"]) == "function") { - xhr.objParam = requestData; - this._resource.generic["delete"].call(this, xhr, this._async); - } - else { - throw new Error("JSDO: DELETE operation is not defined."); - } - } - else { - this._session._openRequest(xhr, 'DELETE', this.url + '/' + id, true); - try { - xhr.send(null); - } catch (e) { - request.success = false; - request.exception = e; - // let Session check for online/offline - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - } - - } - } - - // Synchronize adds - for (var i = 0; i < tableRef._added.length; i++) { - var id = tableRef._added[i]; - var jsrecord = tableRef._findById(id, false); - var requestData = {}; - - if (!jsrecord) continue; - if (tableRef._processed[id]) continue; - tableRef._processed[id] = jsrecord.data; - fireAfterSaveChanges = false; - - var xhr = new XMLHttpRequest(); - xhr.jsdo = this; - var request = { - xhr: xhr, - jsrecord: jsrecord, - batch: batch, - operation: progress.data.JSDO._OP_CREATE, - jsdo: this - }; - batch.operations.push(request); - xhr.onCompleteFn = this._createComplete; - xhr.onSuccessFn = this._createSuccess; - xhr.onErrorFn = this._createError; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - xhr.request = request; - - jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); - this.trigger("beforeCreate", this, jsrecord, request); - - if (this._resource) { - if (typeof(this._resource.generic.create) == "function") { - this._copyRecord(tableRef, jsrecord.data, requestData); - if (this._resource.idProperty !== undefined && jsrecord.data._id !== undefined) { - // Remove _id when idProperty is set - delete requestData._id; - } - - if (tableRef._convertForServer) { - this._convertRowData(tableRef, requestData); - } - - xhr.objParam = requestData; - - this._resource.generic.create.call(this, xhr, this._async); - } - else { - throw new Error("JSDO: CREATE operation is not defined."); - } - - } - else { - this._session._openRequest(xhr, 'POST', this.url, true); - xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); - this._copyRecord(tableRef, jsrecord.data, requestData); - - if (tableRef._convertForServer) { - this._convertRowData(tableRef, requestData); - } - var input = JSON.stringify(requestData); - - try { - xhr.send(input); - } catch (e) { - request.success = false; - request.exception = e; - // let Session check for online/offline - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - } - - } - } - - // Synchronize updates - for (var id in tableRef._changed) { - var jsrecord = tableRef._findById(id, false); - - if (!jsrecord) continue; - if (tableRef._processed[id]) continue; - tableRef._processed[id] = jsrecord.data; - fireAfterSaveChanges = false; - - var xhr = new XMLHttpRequest(); - var request = { - xhr: xhr, - jsrecord: jsrecord, - operation: progress.data.JSDO._OP_UPDATE, - batch: batch, - jsdo: this - }; - xhr.request = request; - xhr.jsdo = this; - batch.operations.push(request); - xhr.onCompleteFn = this._updateComplete; - xhr.onSuccessFn = this._updateSuccess; - xhr.onErrorFn = this._updateError; - xhr.onreadystatechange = this.onReadyStateChangeGeneric; - - jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); - this.trigger("beforeUpdate", this, jsrecord, request); - - var requestData = {}; - if (this._resource.service - && this._resource.service.settings - && this._resource.service.settings.sendOnlyChanges) { - - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData, - tableRef._beforeImage[jsrecord.data._id]); - - if (this._resource.idProperty) { - requestData[this._resource.idProperty] = jsrecord.data[this._resource.idProperty]; - } - else { - throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, - " for sendOnlyChanges property")); - } - } - else { - // We must copy record in case _convertRowData() needs to make conversion - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, requestData); - } - - if (tableRef._convertForServer) { - this._convertRowData(tableRef, requestData); - } - - if (this._resource) { - if (typeof(this._resource.generic.update) == "function") { - xhr.objParam = requestData; - this._resource.generic.update.call(this, xhr, this._async); - } - else { - throw new Error("JSDO: UPDATE operation is not defined."); - } - } - else { - this._session._openRequest(xhr, 'PUT', this.url + '/' + id, this._async); - xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); - - var input = JSON.stringify(requestData); - - try { - xhr.send(input); - } catch (e) { - request.success = false; - request.exception = e; - // let Session check for online/offline - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - } - } - } - - if (this.autoApplyChanges) { - // Arrays to keep track of changes - tableRef._added = []; - tableRef._changed = {}; - tableRef._deleted = []; - tableRef._processed = {}; - } - - // OE00229270 If _async is false, fire afterSaveChanges() after all operations are processed - if (!this._async) - fireAfterSaveChanges = true; - - if (fireAfterSaveChanges) { - var jsdo = this; - var request = { - batch: batch, - success: true - }; - - // Save error messages - jsdo._lastErrors = []; - if (batch.operations) { - jsdo._updateLastErrors(jsdo, batch, null); - } - - jsdo._undefWorkingRecord(); - jsdo._fireAfterSaveChanges(request.success, request); - } - - return promise; - }; - - /************************************************************************ - * - * Synchronizes changes for a DataSet or a temp-table, sending over the entire change-set - * to saveChanges() on server - * If sync'ing a DataSet, sends over before-image and after-image data. - */ - this._syncDataSetForSubmit = function (request) { - var deferred, - promise, - jsonObject, - completeFn = this._saveChangesComplete, - successFn = this._saveChangesSuccess, - errorFn = this._saveChangesError; - - if (typeof($) == 'function' && typeof($.Deferred) == 'function') { - deferred = $.Deferred(); - promise = deferred.promise(); - request.deferred = deferred; - } - - request.jsrecords = []; - - // First thing to do is to create jsonObject with before and after image data for all - // records in change-set (creates, updates and deletes) - if ( this._dataSetName ) { - jsonObject = this._createChangeSet(this._dataSetName, false, request); - } - else { - // just a temp-table. Need to create it somewhat differently from DS - // (no before and after image data) - jsonObject = this._createTTChangeSet(this._defaultTableRef, request); - successFn = this._saveChangesSuccessTT; // will process success response differently from DS - } - - this._execGenericOperation(progress.data.JSDO._OP_SUBMIT, jsonObject, request, - completeFn, successFn, errorFn); - - return promise; - }; - - /************************************************************************ - * - * Private method that creates a jsonObject with before and after image data for all - * records in change-set (creates, updates and deletes) - * - * Params: dataSetName is required. - * alwaysCreateTable is required. If true, always create table array (even if no data/changes) - * request is optional - */ - this._createChangeSet = function (dataSetName, alwaysCreateTable, request) { - var changeSetJsonObject = {}; - - changeSetJsonObject[dataSetName] = {}; - var dataSetJsonObject = changeSetJsonObject[dataSetName]; - - var hasChanges = dataSetJsonObject["prods:hasChanges"] = this._hasChanges(); - if (hasChanges) { - if ((alwaysCreateTable === true)) { - for (var buf in this._buffers) { - dataSetJsonObject[this._buffers[buf]._name] = []; - } - } - - // First do deletes - //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._addDeletesToChangeSet(tableRef, dataSetJsonObject, request); - } - - // Adds - //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._addCreatesToChangeSet(tableRef, dataSetJsonObject, request); - } - - // Updates - //for (var buf in this._buffers) { this._buffers[buf]._visited = false; } - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._addChangesToChangeSet(tableRef, dataSetJsonObject, request); - } - - // Clear _processed map - for (var buf in this._buffers) { - this._buffers[buf]._processed = {}; - } - } - - // Check if change set is empty - // A saveChanges() with a delete of new record would result in an empty change set - // An empty DataSet is sent to the server to ensure that AfterSaveChanges fires - var keys = Object.keys(changeSetJsonObject[dataSetName]); - if (keys.length == 1 && keys[0] == "prods:hasChanges") { - for (var buf in this._buffers) { - dataSetJsonObject[this._buffers[buf]._name] = []; - } - dataSetJsonObject["prods:hasChanges"] = false; - } - - return changeSetJsonObject; - }; - - /************************************************************************ - * - * Private method that creates a jsonObject for the created and changed records - * in a temp-table. There is no before-image information. This is used in the - * case of a Submit operation when the JSDO is just for a temp-table - * - * Params: dataSetName is required. - * alwaysCreateTable is required. If true, always create table array (even if no data/changes) - * request is optional - */ - this._createTTChangeSet = function (tableRef, request) { - var changeSetJsonObject = {}, - hasChanges, - tempTableJsonObject, - i, - id, - jsrecord; - - changeSetJsonObject[tableRef._name] = []; - tempTableJsonObject = changeSetJsonObject[tableRef._name]; - - hasChanges = this._hasChanges(); - if (hasChanges) { - - // (note that we do not send deleted rows on submit for a temp-table) - - // Adds - for (i = 0; i < tableRef._added.length; i++) { - id = tableRef._added[i]; - jsrecord = tableRef._findById(id, false); - if (jsrecord) { - if ( !tableRef._processed[jsrecord.data._id] ) { - this._addRowToTTChangeSet(tableRef, jsrecord, tempTableJsonObject, - request, "beforeCreate"); - } - } - } - - // changed rows - for (id in tableRef._changed) { - if (tableRef._changed.hasOwnProperty(id)) { - jsrecord = tableRef._findById(id, false); - if (jsrecord) { - if ( !tableRef._processed[jsrecord.data._id] ) { - this._addRowToTTChangeSet(tableRef, jsrecord, tempTableJsonObject, - request, "beforeUpdate"); - } - } - } - } - - // Clear _processed map - tableRef._processed = {}; - } - - return changeSetJsonObject; - }; - - this._addRowToTTChangeSet = function (tableRef, jsrecord, tempTableJsonObject, request, event) { - var rowData = {}; - - tableRef._processed[jsrecord.data._id] = jsrecord.data; - - // Store jsrecord in request object so we can access it when saveChanges completes, - // in order to run afterCreate events - if (typeof(request) != 'undefined') { - request.jsrecords.push(jsrecord); - - // Need to call beforeCreate trigger when saveChanges(true) is called - jsrecord._tableRef.trigger(event, this, jsrecord, request); - this.trigger(event, this, jsrecord, request); - } - - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - delete rowData["_id"]; - - tempTableJsonObject.push(rowData); - }; - - /************************************************************************ - * - * Private method that creates a jsonObject with data and also before image data - * for all records in change-set (creates, updates and deletes) - * - * Params: dataSetName is required. - * It returns jsonObject that can be used as input to addRecords() - */ - this._createDataAndChangeSet = function (dataSetName) { - var jsonObject = {}; - - jsonObject[dataSetName] = {}; - var dataSetJsonObject = jsonObject[dataSetName]; - - /* We always want to create tables (even if there's no data) so we can compare schemas - * of data in local storage to JSDO's schema */ - for (var buf in this._buffers) - dataSetJsonObject[this._buffers[buf]._name] = []; - - if (this._hasChanges()) { - dataSetJsonObject["prods:hasChanges"] = true; - } - - // Add data from each table. This will also add bi data for any created or updated rows - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._addRecordsToObject(tableRef, dataSetJsonObject); - } - - // Now do deletes - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - this._addDeletesToChangeSet(tableRef, dataSetJsonObject); - } - - // Clear _processed map - for (var buf in this._buffers) { - this._buffers[buf]._processed = {}; - } - return jsonObject; - }; - - // This method adds all record for specified table into dataSetJsonObject. - // If record has bi data, it adds that as well - this._addRecordsToObject = function (tableRef, dataSetJsonObject) { - - if (tableRef._data.length > 0 && !dataSetJsonObject[tableRef._name]) - dataSetJsonObject[tableRef._name] = []; - - for (var i = 0; i < tableRef._data.length; i++) { - var record = tableRef._data[i]; - if (!record) continue; - - // Check if record has bi data, can only determine if it's created or changed since - // deleted rows are not in after data - if (this._doesRecordHaveCreateBIData(tableRef, record._id) === true) { - var jsrecord = tableRef._findById(record._id, false); - if (!jsrecord) continue; - if (tableRef._processed[jsrecord.data._id]) continue; - this._addCreatedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject); - } - if (this._doesRecordHaveUpdateBIData(tableRef, record._id) === true) { - var jsrecord = tableRef._findById(record._id, false); - if (!jsrecord) continue; - if (tableRef._processed[jsrecord.data._id]) continue; - this._addChangedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject); - } - else { - if (tableRef._processed[record._id]) continue; - tableRef._processed[record._id] = record; - - var rowData = {}; - - tableRef._jsdo._copyRecord(tableRef, record, rowData); - delete rowData["_id"]; - - dataSetJsonObject[tableRef._name].push(rowData); - } - } - }; - - - // Check if specified after record has bi data for newly created record. - // Returns True if after record has corresponding bi data, else false - this._doesRecordHaveCreateBIData = function (tableRef, id) { - for (var i = 0; i < tableRef._added.length; i++) { - if (tableRef._added[i] === id) - return true; - } - - return false; - }; - - // Check if specified after record has bi data for updated record. - // Returns True if after record has corresponding bi data, else false - this._doesRecordHaveUpdateBIData = function (tableRef, id) { - for (var changedId in tableRef._changed) { - if (changedId === id) - return true; - } - - return false; - }; - - - // If a create, remove or update exists, method returns true, else returns false - this._hasChanges = function () { - var hasChanges = false; - - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - - var hasUpdates = false; - for (var id in tableRef._changed) { - hasUpdates = true; - break; - } - - if (tableRef._deleted.length > 0 || tableRef._added.length > 0 || hasUpdates) { - hasChanges = true; - break; - } - } - - return hasChanges; - }; - - // This method is used when saveChanges() is called, and also when storing data to local storage. - // The request param should be defined for saveChanges(), - // but not needed when storing data to local storage - this._addDeletesToChangeSet = function (tableRef, dataSetJsonObject, request) { - // There is no after table for deletes, so just create before-table data - for (var i = 0; i < tableRef._deleted.length; i++) { - var jsrecord = tableRef._deleted[i]; - - if (!jsrecord) continue; - - if (jsrecord.data - && jsrecord.data._id !== undefined - && tableRef._beforeImage[jsrecord.data._id] === null) { - // Deleted record is for a new record - do not send deleted record to server - continue; - } - - this._addDeletedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); - } - }; - - this._addDeletedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { - tableRef._processed[jsrecord.data._id] = jsrecord.data; - - // Store jsrecord in request object so we can access it when saveChanges completes, - // in order to run afterDelete events - jsrecord.data["prods:rowState"] = "deleted"; - - if (typeof(request) != 'undefined') { - request.jsrecords.push(jsrecord); - - // Need to call beforeDelete trigger if saveChanges(true) is called - jsrecord._tableRef.trigger("beforeDelete", this, jsrecord, request); - this.trigger("beforeDelete", this, jsrecord, request); - } - - var beforeRowData = {}; - // AppServer will roundtrip this back to jsdo client - beforeRowData["prods:clientId"] = jsrecord.data._id; - beforeRowData["prods:rowState"] = "deleted"; - - var beforeTableJsonObject = this._getTableInBeforeJsonObject(dataSetJsonObject, tableRef._name); - tableRef._jsdo._copyRecord(tableRef, tableRef._beforeImage[jsrecord.data._id], beforeRowData); - delete beforeRowData["_id"]; - - beforeTableJsonObject.push(beforeRowData); - }; - - // This method is used when saveChanges() is called, and also when storing data to local storage. - // The request param should be defined for saveChanges(), - // but not needed when storing data to local storage - this._addCreatesToChangeSet = function (tableRef, dataSetJsonObject, request) { - // There is no before table for creates, so just create after-table data - for (var i = 0; i < tableRef._added.length; i++) { - var id = tableRef._added[i]; - var jsrecord = tableRef._findById(id, false); - if (!jsrecord) continue; - if (tableRef._processed[jsrecord.data._id]) continue; - - this._addCreatedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); - } - }; - - this._addCreatedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { - tableRef._processed[jsrecord.data._id] = jsrecord.data; - - if (!dataSetJsonObject[tableRef._name]) { - dataSetJsonObject[tableRef._name] = []; - } - - // Store jsrecord in request object so we can access it when saveChanges completes, - // in order to run afterCreate events - jsrecord.data["prods:rowState"] = "created"; - - if (typeof(request) != 'undefined') { - request.jsrecords.push(jsrecord); - - // Need to call beforeCreate trigger when saveChanges(true) is called - jsrecord._tableRef.trigger("beforeCreate", this, jsrecord, request); - this.trigger("beforeCreate", this, jsrecord, request); - } - - var rowData = {}; - // AppServer will roundtrip this back to jsdo client - rowData["prods:clientId"] = jsrecord.data._id; - rowData["prods:rowState"] = "created"; - - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - delete rowData["_id"]; - - dataSetJsonObject[tableRef._name].push(rowData); - }; - - // This method is used when saveChanges() is called, and also when storing data to local storage. - // The request param should be defined for saveChanges(), - // but not needed when storing data to local storage - this._addChangesToChangeSet = function (tableRef, dataSetJsonObject, request) { - // For Changes, there is both before and after table data - for (var id in tableRef._changed) { - var jsrecord = tableRef._findById(id, false); - if (!jsrecord) continue; - if (tableRef._processed[jsrecord.data._id]) continue; - - this._addChangedRowToChangeSet(tableRef, jsrecord, dataSetJsonObject, request); - } - }; - - this._addChangedRowToChangeSet = function (tableRef, jsrecord, dataSetJsonObject, request) { - tableRef._processed[jsrecord.data._id] = jsrecord.data; - - if (!dataSetJsonObject[tableRef._name]) { - dataSetJsonObject[tableRef._name] = []; - } - - // Store jsrecord in request object so we can access it when saveChanges completes, in order - // to run afterUpdate events - jsrecord.data["prods:rowState"] = "modified"; - - if (typeof(request) != 'undefined') { - request.jsrecords.push(jsrecord); - - // Need to call beforeUpdate trigger when saveChanges(true) is called - jsrecord._tableRef.trigger("beforeUpdate", this, jsrecord, request); - this.trigger("beforeUpdate", this, jsrecord, request); - } - - var rowData = {}; - // Required by AppServer in before-image data. Matches before row - rowData["prods:id"] = jsrecord.data._id; - // AppServer will roundtrip this back to jsdo client - rowData["prods:clientId"] = jsrecord.data._id; - rowData["prods:rowState"] = "modified"; - - tableRef._jsdo._copyRecord(tableRef, jsrecord.data, rowData); - delete rowData["_id"]; - - dataSetJsonObject[tableRef._name].push(rowData); - - // Now add before-image data - var beforeTableJsonObject = this._getTableInBeforeJsonObject(dataSetJsonObject, tableRef._name); - var beforeRowData = {}; - // Required by AppServer in before-image data. Matches after row - beforeRowData["prods:id"] = jsrecord.data._id; - - tableRef._jsdo._copyRecord(tableRef, tableRef._beforeImage[jsrecord.data._id], beforeRowData); - //delete beforeRowData["_id"]; - - beforeTableJsonObject.push(beforeRowData); - }; - - - // Private method to get table's json object from the specified dataset json object. - // If it hasn't been created yet, this method creates it. - this._getTableInBeforeJsonObject = function (dataSetJsonObject, tableName) { - if (!dataSetJsonObject["prods:before"]) { - dataSetJsonObject["prods:before"] = {}; - } - var beforeObject = dataSetJsonObject["prods:before"]; - - if (!beforeObject[tableName]) { - beforeObject[tableName] = []; - } - - return beforeObject[tableName]; - }; - - - /********************************************************************* - * - * Reads a JSON object into the JSDO memory. - */ - this.addRecords = function (jsonObject, addMode, keyFields, trackChanges, isInvoke) { - if (this.isDataSet()) { - if (jsonObject instanceof Array) { - if (!this._defaultTableRef) { - throw new Error(msg.getMsgText("jsdoMSG998")); - } - } - else { - if (jsonObject === undefined || jsonObject === null) { - jsonObject = {}; - } - - if (jsonObject[this._dataSetName]) { - jsonObject = jsonObject[this._dataSetName]; - } - } - - // Allow empty object in addRecords with MODE_EMPTY - if (addMode != progress.data.JSDO.MODE_EMPTY) { - if (Object.keys(jsonObject).length === 0) - throw new Error(msg.getMsgText("jsdoMSG006")); - } - - var oldUseRelationships = this.useRelationships; - // Turn off useRelationships since addRecords() does not use the working record - this.useRelationships = false; - try { - for (var buf in this._buffers) { - // Read data for tables in JSON object - if (jsonObject[this._buffers[buf]._name]) - this._addRecords(this._buffers[buf]._name, jsonObject, addMode, - keyFields, trackChanges, isInvoke); - else if (addMode == progress.data.JSDO.MODE_EMPTY) { - this._buffers[this._buffers[buf]._name]._clearData(); - } - } - } finally { - // Restore useRelationships - this.useRelationships = oldUseRelationships; - } - } - else if (this._defaultTableRef) { - this._addRecords(this._defaultTableRef._name, jsonObject, addMode, keyFields, - trackChanges, isInvoke); - } - }; - - /* - * Copies the fields of the source record to the target record. - * Preserves the _id of the target record. - */ - this._copyRecord = function (tableRef, source, target, onlyChangesRecord) { - for (var field in source) { - - if (onlyChangesRecord !== undefined) { - if (source[field] == onlyChangesRecord[field]) - continue; - } - - // Fix for PSC00277769 - if (source[field] === undefined || source[field] === null) { - target[field] = source[field]; - } - else if (source[field] instanceof Date) { - target[field] = source[field]; - } - else if (typeof source[field] === 'object') { - var newObject = source[field] instanceof Array ? [] : {}; - this._copyRecord(tableRef, source[field], newObject); - target[field] = newObject; - } - else - target[field] = source[field]; - } - }; - - /* - * Deletes the "prods:" properties when no longer needed, - * typically when doing acceptChanges, rejectChanges, or _applyChanges. - * These properties are used to transfer before-image info between client JSDO and AppServer. - * - * Also, it optionally clears out the errorString field depending upon value of clearErrorString. - * To be consistent with the handling of - * the ABL's Buffer ERROR-STRING attribute, - * the errorString field should be cleared out when doing acceptChanges() or rejectChanges(). - */ - this._deleteProdsProperties = function (record, clearErrorString, deleteRowState) { - - /* Default to false */ - if (typeof(clearErrorString) == 'undefined') { - clearErrorString = false; - } - - /* Default to true */ - if (typeof(deleteRowState) == 'undefined') { - deleteRowState = true; - } - - if (record) { - delete record["prods:id"]; - delete record["prods:hasErrors"]; - delete record["prods:clientId"]; - delete record["prods:rejected"]; - delete record._rejected; - - if (deleteRowState) { - delete record["prods:rowState"]; - } - - if (clearErrorString) { - delete record._errorString; - } - } - }; - - this._addRecords = function (tableName, jsonObject, addMode, keyFields, trackChanges, isInvoke) { - var beforeImageJsonObject = null; - var beforeImageJsonIndex = null; - - if (jsonObject && (this._dataSetName !== undefined)) { - if (jsonObject[this._dataSetName] && - jsonObject[this._dataSetName]["prods:hasChanges"]) { - beforeImageJsonObject = jsonObject; - beforeImageJsonIndex = {}; - } - else if (jsonObject["prods:hasChanges"]) { - beforeImageJsonObject = {}; - beforeImageJsonObject[this._dataSetName] = jsonObject; - beforeImageJsonIndex = {}; - } - } - - if (typeof(tableName) != 'string') - throw new Error(msg.getMsgText("jsdoMSG020")); - if (!addMode) - throw new Error(msg.getMsgText("jsdoMSG021")); - - switch (addMode) { - case progress.data.JSDO.MODE_APPEND: - case progress.data.JSDO.MODE_EMPTY: - case progress.data.JSDO.MODE_MERGE: - case progress.data.JSDO.MODE_REPLACE: - break; - default: - throw new Error(msg.getMsgText("jsdoMSG022")); - } - - if (!keyFields) - keyFields = []; - else { - if (!(keyFields instanceof Array) && (typeof(keyFields) == 'object')) { - if (keyFields[tableName]) { - keyFields = keyFields[tableName]; - } - else { - keyFields = []; - } - } - } - - if (!(keyFields instanceof Array)) { - throw new Error(msg.getMsgText("jsdoMSG008")); - } - - // Check that the specified field names are in the schema - if (this._buffers[tableName]._fields) { - for (var i = 0; i < keyFields.length; i++) { - var field = this._buffers[tableName]._fields[keyFields[i].toLowerCase()]; - if (field === undefined) { - throw new Error(msg.getMsgText("jsdoMSG009", keyFields[i])); - } - else { - keyFields[i] = field.name; - } - } - } - - trackChanges = trackChanges ? true : false; - - if (tableName) { - if (!(jsonObject instanceof Array)) { - var data = null; - - if (jsonObject === undefined || jsonObject === null) { - jsonObject = {}; - } - - if (this.isDataSet()) { - if (jsonObject[this._dataSetName]) - data = jsonObject[this._dataSetName][tableName]; - else if (jsonObject[tableName]) - data = jsonObject[tableName]; - } else { - if (this._dataProperty) - data = jsonObject[this._dataProperty]; - else if (jsonObject.data) - data = jsonObject.data; - } - - - if (data instanceof Array) { - saveJsonObject = jsonObject; - jsonObject = data; - } - else if ((addMode == progress.data.JSDO.MODE_EMPTY) - && (typeof (jsonObject) == 'object') - && (Object.keys(jsonObject).length === 0)) { - jsonObject = []; // Allow empty object in addRecords with - // MODE_EMPTY - } - // Allow empty object when called by restoreChangesOnlyForTable() - // where there are only deletes - in bi data - else if ((addMode == progress.data.JSDO.MODE_REPLACE) - && (typeof (jsonObject) == 'object') - && (beforeImageJsonObject)) { - jsonObject = []; - } - } - - if (!(jsonObject instanceof Array)) { - throw new Error(msg.getMsgText("jsdoMSG005", tableName)); - } - - var dataHasBeenProcessed = false; - try { - this._buffers[tableName]._sortRecords = false; - if (keyFields.length === 0 || addMode == progress.data.JSDO.MODE_EMPTY) { - // Quick merge - if (addMode == progress.data.JSDO.MODE_EMPTY) { - this._buffers[tableName]._clearData(); - } - // APPEND, MERGE, REPLACE - for (var i = 0; i < jsonObject.length; i++) { - var jsrecord = this._buffers[tableName]._add(jsonObject[i], trackChanges, false); - jsonObject[i]._id = jsrecord.data._id; - if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { - beforeImageJsonIndex[jsonObject[i]["prods:id"]] = jsrecord.data._id; - } - if (beforeImageJsonObject) { - this._deleteProdsProperties(jsrecord.data); - } - } - } - else { - // Build temporary index - var tmpIndex; - - if (this._buffers[tableName]._data.length * jsonObject.length >= 10) { - tmpIndex = {}; - - for (var i = 0; i < this._buffers[tableName]._data.length; i++) { - var record = this._buffers[tableName]._data[i]; - if (!record) continue; - - var key = this._buffers[tableName]._getKey(record, keyFields); - tmpIndex[key] = record; - } - - } - else - tmpIndex = null; // Do not use an index - var checkBeforeImage = - (Object.keys(this._buffers[tableName]._beforeImage).length !== 0); - for (var i = 0; i < jsonObject.length; i++) { - var match = false; - var record = null; - - // Check for duplicates - if (tmpIndex) { - var key = this._buffers[tableName]._getKey(jsonObject[i], keyFields); - record = tmpIndex[key]; - match = (record !== undefined); - } - else { - for (var j = 0; j < this._buffers[tableName]._data.length; j++) { - record = this._buffers[tableName]._data[j]; - if (!record) continue; - match = - (this._buffers[tableName]._equalRecord(jsonObject[i], record, keyFields)); - if (match) { - // Duplicate found - break; - } - } - } - - if (match) { - if (isInvoke - && (this._resource.idProperty !== undefined) - && (jsonObject[i]._id === undefined)) { - // Add _id to jsonObject - jsonObject[i]._id = record._id; - } - - // If beforeRecord is null, there is entry in _beforeImage for a create. - // If beforeRecord is undefined, there is no entry - var beforeRecord = this._buffers[tableName]._beforeImage[record._id]; - if (checkBeforeImage - && (jsonObject[i]["prods:id"] !== undefined) - && (typeof beforeRecord !== 'undefined')) { - // Only throw exception if the existing bi data - // is not the same as the new bi data - var isAfterSame = this._sameData(jsonObject[i], record); - var isBeforeSame = true; - - // For creates, beforeRecord will be null - if (beforeRecord) { - var beforeObject = this._getBeforeRecordFromObject(jsonObject[i], - beforeImageJsonObject, tableName); - if (beforeObject) - isBeforeSame = this._sameData(beforeObject, beforeRecord); - } - - if (!isAfterSame || !isBeforeSame) - throw new Error(msg.getMsgText("jsdoMSG032")); - } - - switch (addMode) { - case progress.data.JSDO.MODE_APPEND: - throw new Error(msg.getMsgText("jsdoMSG023")); - case progress.data.JSDO.MODE_MERGE: - /* Ignore duplicate */ - if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { - beforeImageJsonIndex[jsonObject[i]["prods:id"]] = record._id; - } - break; - case progress.data.JSDO.MODE_REPLACE: - if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { - beforeImageJsonIndex[jsonObject[i]["prods:id"]] = record._id; - } - - if (jsonObject[i]._id === undefined) - jsonObject[i]._id = record._id; - this._copyRecord( - this._buffers[tableName], - jsonObject[i], record); - this._deleteProdsProperties(record); - break; - default: - break; - } - } - else { - // Add record - var jsrecord = - this._buffers[tableName]._add(jsonObject[i], trackChanges, false); - jsonObject[i]._id = jsrecord.data._id; - if (beforeImageJsonIndex && jsonObject[i]["prods:id"]) { - beforeImageJsonIndex[jsonObject[i]["prods:id"]] = jsrecord.data._id; - } - if (beforeImageJsonObject) { - this._deleteProdsProperties(jsrecord.data); - } - if (tmpIndex) { - var key = this._buffers[tableName]._getKey(jsrecord.data, keyFields); - tmpIndex[key] = jsrecord.data; - } - } - - } - tmpIndex = null; - } - dataHasBeenProcessed = true; - } - finally { - this._buffers[tableName]._sortRecords = true; - this._buffers[tableName]._sort(); - this._buffers[tableName]._createIndex(); - - if (dataHasBeenProcessed && beforeImageJsonObject) { - this._buffers[tableName]._loadBeforeImageData(beforeImageJsonObject, - beforeImageJsonIndex, keyFields); - } - } - } - }; - - // This method returns corresponding bi record of the afterRecord from specified jsonObject - this._getBeforeRecordFromObject = function (afterRecord, jsonObject, tablename) { - var beforeData = jsonObject[this._dataSetName]["prods:before"]; - var id = afterRecord["prods:id"]; - var beforeRecord; - - if (!beforeData) return beforeRecord; - - // First check to see if the before data is the same - for (var i = 0; i < beforeData[tablename].length; i++) { - var record = beforeData[tablename][i]; - if (record["prods:id"] && id == record["prods:id"]) { - beforeRecord = record; - break; - } - } - - return beforeRecord; - }; - - this._sameData = function (record1, record2) { - var value1, value2; - for (var fieldName in record1) { - if (fieldName.substring(0, 5) != "prods" && fieldName != "_id") { - value1 = record1[fieldName]; - value2 = record2[fieldName]; - - if (value1 > value2 || value1 === null) - return false; - else if (value1 < value2 || value2 === null) - return false; - } - } - - return true; - }; - - - // private method to merge changes after a read operation - this._mergeRead = function (jsonObject, xhr) { - if (this.isDataSet()) { - if (this._dataProperty) { - var datasetBuffer = this._buffers[this._dataProperty]; - datasetBuffer._data = jsonObject[this._dataSetName][this._dataProperty]; - if (datasetBuffer.autoSort) { - datasetBuffer._sort(); - } - datasetBuffer._createIndex(); - } - else { - // Load data from JSON object into _data - for (var buf in this._buffers) { - var data; - if (jsonObject[this._dataSetName]) - data = jsonObject[this._dataSetName][buf]; - else - data = null; - data = data ? data : []; - this._buffers[buf]._data = data; - if (this._buffers[buf].autoSort) { - this._buffers[buf]._sort(); - } - this._buffers[buf]._createIndex(); - if (jsonObject[this._dataSetName] - && jsonObject[this._dataSetName]["prods:hasChanges"]) { - this._buffers[buf]._loadBeforeImageData(jsonObject); - } - } - // Load nested data into _data - if (this._numBuffers > 1) { - for (var buf in this._buffers) { - if (this._buffers[buf]._isNested - && this._buffers[buf]._parent - && this._buffers[this._buffers[buf]._parent]) { - var srcData = this._buffers[this._buffers[buf]._parent]._data; - var data = []; - for (var i = 0; i < srcData.length; i++) { - if (srcData[i][buf] !== undefined) { - for (var j = 0; j < srcData[i][buf].length; j++) { - data.push(srcData[i][buf][j]); - } - delete srcData[i][buf]; - } - } - this._buffers[buf]._data = data; - if (this._buffers[buf].autoSort) { - this._buffers[buf]._sort(); - } - this._buffers[buf]._createIndex(); - } - } - } - } - } - else { - if (jsonObject instanceof Array) { - this._defaultTableRef._data = jsonObject; - } - else { - if (this._dataProperty) - this._defaultTableRef._data = jsonObject[this._dataProperty]; - else if (jsonObject.data) - this._defaultTableRef._data = jsonObject.data; - else { - this._defaultTableRef._data = []; - this._defaultTableRef._data[0] = jsonObject; - } - } - } - - for (var buf in this._buffers) { - if (this._buffers[buf].autoSort) { - this._buffers[buf]._sort(); - } - this._buffers[buf]._createIndex(); - } - }; - - /** - * Replace existing record data and index entry with new record data. - */ - this._mergeUpdateRecord = function (tableRef, recordId, record) { - var index = tableRef._index[recordId].index; - record._id = recordId; - - if (!tableRef._data[index]) { - tableRef._data[index] = {}; - } - this._copyRecord(this._tableRef, record, tableRef._data[index]); - record = tableRef._data[index]; - - if (tableRef._jsdo._resource.idProperty !== undefined) { - var id = tableRef._data[index][tableRef._jsdo._resource.idProperty]; - if (id !== undefined) { - delete tableRef._index[recordId]; - id += ""; - tableRef._index[id] = new progress.data.JSIndexEntry(index); - record._id = id; - } - } - - return record; - }; - - - /** - *update existing record data with specified error string - */ - this._setErrorString = function (tableRef, recordId, errorString, setInBeforeTable) { - - if (setInBeforeTable) { - // Ensure that object exists, it's null for deleted rows - if (tableRef._beforeImage[recordId]) { - tableRef._beforeImage[recordId]._errorString = errorString; - } - } - else { - var index = tableRef._index[recordId].index; - tableRef._data[index]._errorString = errorString; - } - }; - - /* - * Returns the array with the data from the specified dataObject. - */ - this._arrayFromDataObject = function (dataObject, tableRef) { - var data; - - if (dataObject === undefined) return undefined; - if (this._dataSetName) { - if (dataObject[this._dataSetName]) - data = dataObject[this._dataSetName][tableRef._name]; - } - else { - // check if data returned as array - if (dataObject instanceof Array) { - data = dataObject; - } else { - // or if data property is set - if (this._dataProperty) { - data = dataObject[this._dataProperty]; - } else if (dataObject.data) { - // or just try with 'data' as the data property name - data = dataObject.data; - } - } - } - - return data; - }; - - ///////////////////////////////////////////////////////////////////////////////////////////// - // - // Private method to merge changes after a create or update operation. - // This method is called to merge changes when server's Create or Update methods were called. - // - // It returns true if it found error for row in before-image data (prods:hasErrors = true) - // It returns false if there is no before-image data or prods:hasErrors property is absent - this._mergeUpdateForCUD = function (jsonObject, xhr) { - var hasError = false, - errorString; - - // Update dataset with changes from server - if (this._dataSetName) { - var dataSetJsonObject = jsonObject[this._dataSetName]; - - // only updates the specified record - var tableRef = xhr.request.jsrecord._tableRef; - var tableJsonObject = this._arrayFromDataObject(jsonObject, tableRef); - - if (tableJsonObject instanceof Array) { - if (tableJsonObject.length > 1) { - xhr.request.success = false; - throw new Error(msg.getMsgText("jsdoMSG100")); - } - - for (var i = 0; i < tableJsonObject.length; i++) { - var recordId = xhr.request.jsrecord.getId(); - - if (!recordId) { - throw new Error(msg.getMsgText("jsdoMSG034", "_mergeUpdateForCUD()")); - } - - // Determine if error string (get prods_id before _mergeUpdateRecord() is called, - // since it removes all prods properties) - errorString = undefined; - - if (tableJsonObject[i]["prods:hasErrors"]) { - var prods_id = tableJsonObject[i]["prods:id"]; - errorString = - this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); - hasError = true; - } - - var record = this._mergeUpdateRecord(tableRef, recordId, tableJsonObject[i]); - if (errorString) - this._setErrorString(tableRef, recordId, errorString, false); - - // Set _rejected property - if (tableJsonObject[i]["prods:rejected"] - || errorString) { - record._rejected = true; - if (errorString === "REJECTED") { - delete record._errorString; - } - } - - xhr.request.jsrecord = new progress.data.JSRecord(tableRef, record); - } - } - } else { - // update single record with changes from server - var tableRef = this._defaultTableRef; - var data = this._arrayFromDataObject(jsonObject); - - if (data instanceof Array) { - if (data.length > 1) { - xhr.request.success = false; - throw new Error(msg.getMsgText("jsdoMSG100")); - } - - for (var i = 0; i < data.length; i++) { - var recordId = xhr.request.jsrecord.getId(); - - if (!recordId) { - throw new Error(msg.getMsgText("jsdoMSG034", "_mergeUpdateForCUD()")); - } - - var record = this._mergeUpdateRecord(tableRef, recordId, data[i]); - xhr.request.jsrecord = new progress.data.JSRecord(tableRef, record); - } - } - } - - return hasError; - }; - - - ///////////////////////////////////////////////////////////////////////////////////////////// - // - // Private method to determine if deleted row (from delete operation) returned from AppServer - // was returned with an error in the before-image data. - // - // It returns true if it found an error for row in before-image data (prods:hasErrors = true) - // It returns false if there is no before-image data or prods:hasErrors property is absent - - this._checkForDeleteError = function (dataSetJsonObject, xhr) { - var hasError = false; - var tableRef = xhr.request.jsrecord._tableRef; - - beforeJsonObject = dataSetJsonObject["prods:before"]; - - // No merge is necessary for deletes, but we need to see - // if there are any errors on deletes records. - // delete records are not in after table, only in before table - if (beforeJsonObject) { - var beforeTableJsonObject = beforeJsonObject[tableRef._name]; - - if (beforeTableJsonObject.length > 1) { - xhr.request.success = false; - throw new Error(msg.getMsgText("jsdoMSG100")); - } - // clientId is same as _id - var recordId = beforeTableJsonObject[0]["prods:clientId"]; - if (!recordId) { - throw new Error(msg.getMsgText("jsdoMSG035", "_checkForDeleteError()")); - } - - // Determine if row was returned with error string - if (beforeTableJsonObject[0]["prods:hasErrors"]) { - var prods_id = beforeTableJsonObject[0]["prods:id"]; - var errorString = - this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); - this._setErrorString(tableRef, recordId, errorString, true); - hasError = true; - } - } - - return hasError; - }; - - ///////////////////////////////////////////////////////////////////////////////////////////// - // - // Private method to merge changes after a call to saveChanges. - // This method is called when saveChanges(useSubmit) was called with useSubmit=true. - // This can process/merge one or more created, deleted or updated records. - // In order for a jsonObject to have before-image data, it must be associated with a dataset. - // - // It only merges changes in the after table. But we need to look at before-image table to see - // if there were any errors passed back for the deletes - // - this._mergeUpdateForSubmit = function (jsonObject, xhr) { - var errorString; - - //if (!this._dataSetName || !jsonObject[this._dataSetName]["prods:hasChanges"]) - if (!this._dataSetName) { - // "_mergeUpdateForSubmit() can only be called for a dataset" - throw new Error(msg.getMsgText("jsdoMSG036", "_mergeUpdateForSubmit()")); - } - - // response is sent back with extra dataset object wrapper - var dataSetJsonObject = jsonObject[this._dataSetName]; - if (dataSetJsonObject[this._dataSetName]) - dataSetJsonObject = dataSetJsonObject[this._dataSetName]; - - var beforeJsonObject = dataSetJsonObject["prods:before"]; - - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - - var tableJsonObject = dataSetJsonObject[tableRef._name]; - if (tableJsonObject instanceof Array) { - for (var i = 0; i < tableJsonObject.length; i++) { - - var recordId = tableJsonObject[i]["prods:clientId"]; - if (!recordId) { - throw new Error(msg.getMsgText("jsdoMSG035", "_mergeUpdateForSubmit()")); - } - - // Determine if error string (get prods_id before _mergeUpdateRecord() is called, - // since it removes all prods properties) - errorString = undefined; - - if (tableJsonObject[i]["prods:hasErrors"]) { - var prods_id = tableJsonObject[i]["prods:id"]; - errorString = - this._getErrorStringFromJsonObject(dataSetJsonObject, tableRef, prods_id); - } - var record = this._mergeUpdateRecord(tableRef, recordId, tableJsonObject[i]); - if (errorString) { - this._setErrorString(tableRef, recordId, errorString, false); - } - - // Set _rejected property so it can be checked in applyChanges() - if (tableJsonObject[i]["prods:rejected"] - || errorString) { - record._rejected = true; - if (errorString === "REJECTED") { - delete record._errorString; - } - } - - // Now need to update jsrecords. - // We use this data when we fire create, update and delete events. - // Updating so that it contains latest data (data sent back from server) - var jsrecords = xhr.request.jsrecords; - for (var idx = 0; idx < jsrecords.length; idx++) { - if (jsrecords[idx].data["_id"] == recordId) { - jsrecords[idx].data = record; - break; - } - } - } - } - } - - // No merge is necessary for deletes, - // but we need to see if there are any errors on deletes records. - // delete records are not in after table, only in before table - if (beforeJsonObject) { - for (var buf in this._buffers) { - var tableRef = this._buffers[buf]; - var beforeTableJsonObject = beforeJsonObject[tableRef._name]; - var errorString; - - if (beforeTableJsonObject instanceof Array) { - for (var i = 0; i < beforeTableJsonObject.length; i++) { - - if (beforeTableJsonObject[i]["prods:rowState"] == "deleted") { - var recordId = beforeTableJsonObject[i]["prods:clientId"]; - if (!recordId) { - throw new Error(msg.getMsgText("jsdoMSG035", "_mergeUpdateForSubmit()")); - } - - errorString = undefined; - // If row was returned with error string, just copy that over to jsdo record - if (beforeTableJsonObject[i]["prods:hasErrors"]) { - var prods_id = beforeTableJsonObject[i]["prods:id"]; - - errorString = this._getErrorStringFromJsonObject(dataSetJsonObject, - tableRef, prods_id); - this._setErrorString(tableRef, recordId, errorString, true); - } - - // Set _rejected property so it can be checked in applyChanges() - if ((beforeTableJsonObject[i]["prods:rejected"] - || errorString) - && tableRef._beforeImage[recordId]) { - tableRef._beforeImage[recordId]._rejected = true; - if (errorString === "REJECTED") { - delete tableRef._beforeImage[recordId]._errorString; - } - } - } - } - } - } - } - }; - - ///////////////////////////////////////////////////////////////////////////////////////////// - // - // Private method that fires afterCreate, afterUpdate and afterDelete (CUD) triggers after - // saveChanges(true) is called. We must fire create, update and delete triggers - // for each record that was sent to backend submit operation - this._fireCUDTriggersForSubmit = function (request) { - // Before firing triggers, delete prods properties (except rowState) so they don't appear in data - for (var idx = 0; idx < request.jsrecords.length; idx++) { - this._deleteProdsProperties(request.jsrecords[idx].data, false, false); - } - - for (var idx = 0; idx < request.jsrecords.length; idx++) { - var jsrecord = request.jsrecords[idx]; - switch (jsrecord.data["prods:rowState"]) { - case "created": - jsrecord._tableRef.trigger("afterCreate", this, jsrecord, request.success, request); - this.trigger("afterCreate", this, jsrecord, request.success, request); - break; - case "modified": - jsrecord._tableRef.trigger("afterUpdate", this, jsrecord, request.success, request); - this.trigger("afterUpdate", this, jsrecord, request.success, request); - break; - case "deleted": - jsrecord._tableRef.trigger("afterDelete", this, jsrecord, request.success, request); - this.trigger("afterDelete", this, jsrecord, request.success, request); - break; - } - } - }; - - ////////////////////////////////////////////////////////////////////////////////////////////// - // - // Private method to return error for specified row - // from jsonObject's prods:errors object (before-data) sent over from AppServer - // - this._getErrorStringFromJsonObject = function (dataSetJsonObject, tableRef, prods_id) { - var tableJsonObject; - var errorsJsonObject = dataSetJsonObject["prods:errors"]; - - if (errorsJsonObject) { - tableJsonObject = errorsJsonObject[tableRef._name]; - } - - if (tableJsonObject instanceof Array) { - for (var i = 0; i < tableJsonObject.length; i++) { - - var id = tableJsonObject[i]["prods:id"]; - if (id === prods_id) { - var errorString = tableJsonObject[i]["prods:error"]; - return errorString === null ? - "Server returned unspecified error. Please check log files." : errorString; - } - } - } - - return undefined; - }; - - this._fillSuccess = function (jsdo, success, request) { - var xhr = request.xhr, - properties; - - // Need to check if responseMapping was specified; developer can specify - // plug-in to manipulate response - properties = jsdo.getMethodProperties("read"); - - if (properties && properties.mappingType) { - mapping = progress.data.PluginManager.getPlugin(properties.mappingType); - if (!mapping) { - throw new Error(progress.data._getMsgText("jsdoMSG118", properties.mappingType)); - } - - if (typeof (mapping.responseMapping) === "function") { - request.response = mapping.responseMapping(jsdo, request.response, { operation: "read" }); - } - } - - jsdo._clearData(); - jsdo._mergeRead(request.response, xhr); - - // Set working record - for (var buf in jsdo._buffers) { - if (!jsdo._buffers[buf]._parent || !jsdo.useRelationships) { - jsdo._buffers[buf]._setRecord(jsdo._buffers[buf]._findFirst()); - } - } - }; - - this._fillComplete = function (jsdo, success, request) { - jsdo.trigger("afterFill", jsdo, request.success, request); - if (request.deferred) { - if (success) { - request.deferred.resolve(jsdo, success, request); - } - else { - request.deferred.reject(jsdo, success, request); - } - } - }; - - this._fillError = function (jsdo, success, request) { - jsdo._clearData(); - jsdo._updateLastErrors(jsdo, null, null, request); - }; - - this._undoCreate = function (tableRef, id) { - // Undo operation - // Remove record from JSDO memory - var entry = tableRef._index[id]; - if (entry !== undefined) { - var index = entry.index; - tableRef._data[index] = null; - } - tableRef._hasEmptyBlocks = true; - delete tableRef._index[id]; - delete tableRef._beforeImage[id]; - // End - Undo operation - }; - - this._undoUpdate = function (tableRef, id, deleteProdsProps) { - /* Default to false */ - if (typeof(deleteProdsProps) == 'undefined') { - deleteProdsProps = false; - } - - // Undo operation - // Restore from before image - var record = tableRef._beforeImage[id]; - - // Before image points to an existing record - if (record) { - var index = tableRef._index[id].index; - tableRef._jsdo._copyRecord(tableRef, record, tableRef._data[index]); - if (deleteProdsProps) - tableRef._jsdo._deleteProdsProperties(tableRef._data[index], true); - } - delete tableRef._beforeImage[id]; - // End - Restore before image - }; - - this._undoDelete = function (tableRef, id, deleteProdsProps) { - /* Default to false */ - if (typeof(deleteProdsProps) == 'undefined') { - deleteProdsProps = false; - } - - // Restore from before image - var record = tableRef._beforeImage[id]; - - // Before image points to an existing record - if (record) { - var index = record._index; - delete record._index; - if (deleteProdsProps) - tableRef._jsdo._deleteProdsProperties(record, true); - - if ((index !== undefined) && (tableRef._data[index] === null)) { - tableRef._data[index] = record; - } - else { - tableRef._data.push(record); - index = tableRef._data.length - 1; - } - tableRef._index[id] = new progress.data.JSIndexEntry(index); - } - delete tableRef._beforeImage[id]; - // End - Restore before image - }; - - this._deleteComplete = function (jsdo, success, request) { - var xhr = request.xhr; - var jsrecord = request.jsrecord; - - try { - // Before firing trigger, delete prods properties so they don't appear in data - jsdo._deleteProdsProperties(jsrecord.data, false); - - jsrecord._tableRef.trigger("afterDelete", jsdo, jsrecord, request.success, request); - jsdo.trigger("afterDelete", jsdo, jsrecord, request.success, request); - - } finally { - request.complete = true; - jsdo._checkSaveComplete(xhr); - } - }; - - this._deleteSuccess = function (jsdo, success, request) { - var xhr = request.xhr; - var jsonObject = request.response; - var beforeJsonObject = null; - var dataSetJsonObject = null; - var data; - - //Even though this is _deleteSuccess, if before-image data is returned, the call of - // delete operation could return a success, but we have to check if error was returned - // in before-image data - var hasError = false; - if (jsdo._useBeforeImage("delete")) { - dataSetJsonObject = jsonObject[jsdo._dataSetName]; - beforeJsonObject = dataSetJsonObject["prods:before"]; - - if (beforeJsonObject) { - data = beforeJsonObject[request.jsrecord._tableRef._name]; - } - } - else { - data = jsdo._arrayFromDataObject(jsonObject, request.jsrecord._tableRef); - } - - if (data instanceof Array) { - if (data.length > 1) { - request.success = false; - throw new Error(msg.getMsgText("jsdoMSG100")); - } - } - - if (beforeJsonObject) { - hasError = jsdo._checkForDeleteError(dataSetJsonObject, xhr); - } - - if (hasError) - request.success = false; - - if (jsdo.autoApplyChanges) { - if (!hasError) { - // Clear before image - delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; - // End - Clear before image - } - else { - jsdo._deleteError(jsdo, success, request); - } - } - }; - - this._deleteError = function (jsdo, success, request) { - if (jsdo.autoApplyChanges) { - jsdo._undoDelete(request.jsrecord._tableRef, request.jsrecord.data._id); - } - }; - - this._createComplete = function (jsdo, success, request) { - var xhr = request.xhr; - var jsrecord = request.jsrecord; - - try { - // Before firing trigger, delete prods properties so they don't appear in data - jsdo._deleteProdsProperties(jsrecord.data, false); - - jsrecord._tableRef.trigger("afterCreate", jsdo, jsrecord, request.success, request); - jsdo.trigger("afterCreate", jsdo, jsrecord, request.success, request); - } finally { - request.complete = true; - jsdo._checkSaveComplete(xhr); - } - }; - - this._createSuccess = function (jsdo, success, request) { - var xhr = request.xhr; - var record = request.response; - var hasError = jsdo._mergeUpdateForCUD(record, xhr); - - if (hasError) - request.success = false; - - if (jsdo.autoApplyChanges) { - if (!hasError) { - // Clear before image - delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; - // End - Clear before image - } - else { - jsdo._createError(jsdo, success, request); - } - } - }; - - this._createError = function (jsdo, success, request) { - if (jsdo.autoApplyChanges) { - jsdo._undoCreate(request.jsrecord._tableRef, request.jsrecord.data._id); - } - }; - - - this._updateComplete = function (jsdo, success, request) { - var xhr = request.xhr; - var jsrecord = request.jsrecord; - try { - // Before firing trigger, delete prods properties so they don't appear in data - jsdo._deleteProdsProperties(jsrecord.data, false); - - jsrecord._tableRef.trigger("afterUpdate", jsdo, jsrecord, request.success, request); - jsdo.trigger("afterUpdate", jsdo, jsrecord, request.success, request); - } finally { - request.complete = true; - jsdo._checkSaveComplete(xhr); - } - }; - - this._updateSuccess = function (jsdo, success, request) { - var xhr = request.xhr; - var hasError = jsdo._mergeUpdateForCUD(request.response, xhr); - - if (hasError) { - request.success = false; - } - - if (jsdo.autoApplyChanges) { - if (!hasError) { - request.success = true; - // Clear before image - delete request.jsrecord._tableRef._beforeImage[request.jsrecord.data._id]; - // End - Clear before image - } - else { - jsdo._updateError(jsdo, success, request); - } - } - }; - - this._updateError = function (jsdo, success, request) { - - if (jsdo.autoApplyChanges) { - request.success = false; - jsdo._undoUpdate(request.jsrecord._tableRef, request.jsrecord.data._id); - } - }; - - - this._saveChangesSuccess = function (jsdo, success, request) { - var records = request.response; - jsdo._mergeUpdateForSubmit(records, request.xhr); - - // Ensure that that the _lastErrors variable has been cleared - jsdo._clearErrors(); - var changes = jsdo.getChanges(); - jsdo._updateLastErrors(jsdo, null, changes); - - jsdo._setAllRecordsRejected(changes); - - if (jsdo.autoApplyChanges) { - jsdo._applyChanges(); - } - }; - - - this._saveChangesError = function (jsdo, success, request) { - jsdo._setAllRecordsRejected(true); - if (jsdo.autoApplyChanges) { - jsdo.rejectChanges(); - } - jsdo._updateLastErrors(jsdo, null, null, request); - }; - - /* _saveChangesSuccessTT - internal function called after a Submit of a temp-table (not DataSet) returns success - This method does not attempt to do any merging of records into the JSDO memory. The - absence of _id for the records means that the only way we could possibly do a "merge" - would be to delete the changed rceords in the JSDO memory and then add the records - that were returned form the data service, but that would invalidate the _id's that - the Kendo datasource depends on. The application programmmer must do the merging in - the afterSaveChanges handler - - *** Submit(temp-table) is not supported. This method will be removed in a future version. *** - */ - this._saveChangesSuccessTT = function (jsdo, success, request) { - var changes; - - // Ensure that that the _lastErrors variable has been cleared - jsdo._clearErrors(); - changes = jsdo.getChanges(); - jsdo._updateLastErrors(jsdo, null, changes); - jsdo._setAllRecordsRejected(false); - }; - - this._saveChangesComplete = function (jsdo, success, request) { - // Success with errors - if ((request.xhr.status >= 200 && request.xhr.status < 300) - && (jsdo._lastErrors.length > 0 || jsdo._someRecordsRejected)) { - request.success = false; - } - - // If saveChanges(true) was called, then we must fire create, update and delete triggers - // for each record that was sent to submit operation - if (jsdo._useSubmit === true) { - jsdo._fireCUDTriggersForSubmit(request); - } - - jsdo._undefWorkingRecord(); - jsdo._fireAfterSaveChanges(request.success, request); - - }; - - this._fireAfterSaveChanges = function (success, request) { - this.trigger("afterSaveChanges", this, success, request); - - if (request.jsrecords) { - if (request.deferred) { - if (success) { - request.deferred.resolve(this, success, request); - } - else { - request.deferred.reject(this, success, request); - } - } - } - else if (request.batch && request.batch.deferred) { - if (success) { - request.batch.deferred.resolve(this, success, request); - } - else { - request.batch.deferred.reject(this, success, request); - } - } - - // Clear error string when autoApplyChanges is true - var clearErrorString = this.autoApplyChanges; - - // This will be set if submit operation was performed - if (request.jsrecords) { - for (var idx = 0; idx < request.jsrecords.length; idx++) { - var jsrecord = request.jsrecords[idx]; - if (clearErrorString) { - delete jsrecord.data._errorString; - } - delete jsrecord.data["prods:rowState"]; - } - } - else if (request.batch && request.batch.operations) { - for (var idx = 0; idx < request.batch.operations.length; idx++) { - var jsrecord = request.batch.operations[idx].jsrecord; - if (clearErrorString) { - delete jsrecord.data._errorString; - } - } - } - }; - - /* - * Returns errors in response associated with the HTTP request.records related to the specified jsrecord. - */ - this._getErrorsFromRequest = function(request) { - var errors = [], // Array of objects with properties: type, id, error, errorNum, responseText - errorArray = [], - errorObject, - retValString, - j, - i; - - if (request && !request.success) { - if (request.xhr.status >= 400 && request.xhr.status < 600) { - try { - responseObject = JSON.parse(request.xhr.responseText); - - // responseText could be an array, an object or just text. - // If it is an array, each object would have properties _errors and optional _retVal. - // If it is not an array, the object would have properties _errors and optional _retVal. - // If it is text, the content could also be an HTML page, this error is handle using "HTTP Status". - if (responseObject instanceof Array) { - errorArray = responseObject; - } else if (responseObject instanceof Object) { - errorArray.push(responseObject); - } - for (i = 0; i < errorArray.length; i += 1) { - errorObject = errorArray[i]; - if (errorObject._retVal) { - errors.push({ - type: progress.data.JSDO.RETVAL, - error: errorObject._retVal - }); - retValString = errorObject._retVal; - } else { - retValString = null; - } - if (errorObject._errors instanceof Array) { - for (j = 0; j < errorObject._errors.length; j += 1) { - if ((errorObject._errors[j]._errorNum === 0) - && (errorObject._errors[j]._errorMsg === retValString)) { - // Suppress additional error msg if it is same as return value - continue; - } - errors.push({ - type: progress.data.JSDO.APP_ERROR, - error: errorObject._errors[j]._errorMsg, - errorNum: errorObject._errors[j]._errorNum - }); - } - } - } - } - catch (e) { - // Ignore exceptions - } - } - if (request.exception) { - errors.push({ - type: progress.data.JSDO.ERROR, - error: request.exception - }); - } - if (errors.length === 0 - && request.xhr - && (request.xhr.status >= 400 && request.xhr.status < 600)) { - errors.push({ - type: progress.data.JSDO.ERROR, - error: "Error: HTTP Status " + request.xhr.status + " " + request.xhr.statusText, - responseText: request.xhr.responseText - }); - } - } - return errors; - }; - - this._updateLastErrors = function (jsdo, batch, changes, request) { - var errors, - errorText, - responseObject, - i, - j, - buf; - - if (batch) { - if (batch.operations === undefined) return; - for (i = 0; i < batch.operations.length; i++) { - request = batch.operations[i]; - if (!request.success && request.xhr) { - if (request.xhr.status >= 200 && request.xhr.status < 300) { - // Add error string to jsdo._lastErrors - jsdo._lastErrors.push({errorString: request.jsrecord.data._errorString}); - // Add error object to jsdo.._lastErrors - jsdo._buffers[request.jsrecord._tableRef._name]._lastErrors.push({ - type: progress.data.JSDO.DATA_ERROR, - id: request.jsrecord.data._id, - error: request.jsrecord.data._errorString}); - } - else { - errors = this._getErrorsFromRequest(request); - errorText = ""; - for (j = 0; j < errors.length; j += 1) { - if (errors.length > 1 && errors[j].error.indexOf("(7243)") != -1) { - // If there are more error messages - // supress error "The Server application has returned an error. (7243)" - continue; - } - // Add error to table reference - if (request.jsrecord - && (errors[j].type === progress.data.JSDO.APP_ERROR - || errors[j].type === progress.data.JSDO.RETVAL)) { - errors[j].id = request.jsrecord.data._id; - request.jsrecord._tableRef._lastErrors.push(errors[j]); - } - if (errorText.length === 0) { - errorText = errors[j].error; - } - else { - errorText += "\n" + errors[j].error; - } - } - // Add error string to jsdo._lastErrors - jsdo._lastErrors.push({errorString: errorText}); - } - } - } - } - else if (changes instanceof Array) { - for (i = 0; i < changes.length; i++) { - if (changes[i].record && changes[i].record.data._errorString !== undefined) { - jsdo._lastErrors.push({errorString: changes[i].record.data._errorString}); - jsdo._buffers[changes[i].record._tableRef._name]._lastErrors.push({ - type: progress.data.JSDO.DATA_ERROR, - id: changes[i].record.data._id, - error: changes[i].record.data._errorString}); - } - } - } - else if (request - && !request.success - && request.xhr - && ((request.xhr.status >= 400 && request.xhr.status < 600) || request.xhr.status === 0)) { - errors = this._getErrorsFromRequest(request); - errorText = ""; - for (j = 0; j < errors.length; j += 1) { - if (errors.length > 1 && errors[j].error.indexOf("(7243)") != -1) { - // If there are more error messages - // supress error "The Server application has returned an error. (7243)" - continue; - } - // Add error to all table references - for (buf in this._buffers) { - this._buffers[buf]._lastErrors.push(errors[j]); - } - if (errorText.length === 0) { - errorText = errors[j].error; - } - else { - errorText += "\n" + errors[j].error; - } - } - jsdo._lastErrors.push({errorString: errorText}); - } - }; - - // Check if all the xhr operations associated with the batch for which - // this xhr object is related have completed (not necessarily to success). - // If all XHR operations have completed this fires 'afterSaveChanges' event - this._checkSaveComplete = function (xhr) { - if (xhr.request) { - var jsdo = xhr.request.jsdo; - var batch = xhr.request.batch; - // OE00229270 Should only do afterSaveChanges if _async - if (jsdo && batch && jsdo._async) { - if (jsdo._isBatchComplete(batch)) { - var success = jsdo._isBatchSuccess(batch); - var request = { - batch: batch, - success: success - }; - jsdo._undefWorkingRecord(); - - // Save error messages - jsdo._lastErrors = []; - if (!success && batch.operations) { - jsdo._updateLastErrors(jsdo, batch, null); - } - this._setAllRecordsRejected(batch); - - jsdo._fireAfterSaveChanges(success, request); - } - } - } - }; - - - /* - * determine if a batch of XHR requests has completed in which all requests are successful - */ - this._isBatchSuccess = function (batch) { - if (batch.operations) { - for (var i = 0; i < batch.operations.length; i++) { - if (!batch.operations[i].success) { - return false; - } - } - } - return true; - }; - - /* - * determine if all XHR requests from the batch of saves have completed (not necessarily to success) - */ - this._isBatchComplete = function (batch) { - if (batch.operations) { - for (var i = 0; i < batch.operations.length; i++) { - var request = batch.operations[i]; - // we have to check against the 'complete' flag because xhr.readyState - // might be set async by the browser - // while we're still in the middle of processing some other requests's response - if (!request.complete) { - return false; - } - } - } - return true; - }; - - this._mergeInvoke = function (jsonObject, xhr) { - var operation; - if (xhr.request.fnName !== undefined - && xhr.jsdo._resource.fn[xhr.request.fnName] !== undefined) { - operation = xhr.jsdo._resource.fn[xhr.request.fnName].operation; - } - else - operation = null; - if (operation === undefined) { - // Operation data is only required for invoke operations with mergeMode: true - operation = null; - for (var i = 0; i < xhr.jsdo._resource.operations.length; i++) { - if (xhr.jsdo._resource.operations[i].name == xhr.request.fnName) { - operation = xhr.jsdo._resource.operations[i]; - break; - } - } - xhr.jsdo._resource.fn[xhr.request.fnName].operation = operation; - } - if (operation !== null && operation.mergeMode) { - try { - var mergeMode = progress.data.JSDO["MODE_" + operation.mergeMode.toUpperCase()]; - if (mergeMode === null) { - throw new Error(msg.getMsgText("jsdoMSG030", "mergeMode property", - "EMPTY, APPEND, MERGE or REPLACE")); - } - if (xhr.jsdo._resource.idProperty === undefined) { - throw new Error(msg.getMsgText("jsdoMSG110", this._resource.name, - " by mergeMode property in invoke operation")); - } - var dataParameterName; - if (xhr.jsdo.isDataSet()) { - dataParameterName = xhr.jsdo._resource._dataSetName; - } - else if (xhr.jsdo._resource.dataProperty !== undefined) { - dataParameterName = xhr.jsdo._resource.dataProperty; - } - else if (xhr.jsdo._resource._tempTableName !== undefined) { - dataParameterName = xhr.jsdo._resource._tempTableName; - } - else { - throw new Error(msg.getMsgText("jsdoMSG111", "")); - } - - var found = false; - for (var i = 0; i < operation.params.length; i++) { - if (operation.params[i].name == dataParameterName) { - if (operation.params[i].type.indexOf('RESPONSE_BODY') != -1) { - if ((operation.params[i].xType !== undefined) - && (operation.params[i].xType != 'DATASET') - && (operation.params[i].xType != 'TABLE') - && (operation.params[i].xType != 'ARRAY')) { - throw new Error(msg.getMsgText("jsdoMSG113", operation.params[i].xType, - dataParameterName, xhr.request.fnName)); - } - found = true; - break; - } - } - } - - if (!found) { - throw new Error(msg.getMsgText("jsdoMSG112", dataParameterName, xhr.request.fnName)); - } - xhr.jsdo.addRecords(xhr.request.response[dataParameterName], - mergeMode, [xhr.jsdo._resource.idProperty], false, true); - } - catch (e) { - xhr.request.success = false; - xhr.request.exception = e; - } - } - }; - - this.onReadyStateChangeGeneric = function () { - var xhr = this; - if (xhr.readyState == 4) { - var request = xhr.request; - - /* try to parse response even if request is considered "failed" due to http status */ - try { - request.response = JSON.parse(xhr.responseText); - // in some cases the object back from appserver has a "response" property which represents - // the real content of the JSON...happens when multiple output parameters are returned. - // this of course assumes no one names their root object "response". - if (request.response && request.response.response) { - request.response = request.response.response; - } - } catch (e) { - request.response = undefined; - } - - try { - if ((xhr.status >= 200 && xhr.status < 300) - || (xhr.status === 0 && xhr.responseText !== "")) { - - request.success = true; - // get the Client Context ID (AppServer ID) - xhr.jsdo._session._saveClientContextId(xhr); - if ((typeof xhr.onSuccessFn) == 'function') { - var operation; - if (xhr.request.fnName !== undefined - && xhr.jsdo._resource.fn[xhr.request.fnName] !== undefined) { - operation = xhr.jsdo._resource.fn[xhr.request.fnName].operation; - } - else - operation = null; - if ((operation === undefined) || (operation !== null && operation.mergeMode)) - xhr.jsdo._mergeInvoke(request.response, xhr); - if (request.success) - xhr.onSuccessFn(xhr.jsdo, request.success, request); - else if ((typeof xhr.onErrorFn) == 'function') - xhr.onErrorFn(xhr.jsdo, request.success, request); - } - - } else { - request.success = false; - if (xhr.status === 0) { - request.exception = new Error(msg.getMsgText("jsdoMSG101")); - } - if ((typeof xhr.onErrorFn) == 'function') { - xhr.onErrorFn(xhr.jsdo, request.success, request); - } - } - } catch (e) { - request.success = false; - request.exception = e; - if ((typeof xhr.onErrorFn) == 'function') { - xhr.onErrorFn(xhr.jsdo, request.success, request); - } - } - // get the Client Context ID (AppServer ID) - xhr.jsdo._session._checkServiceResponse(xhr, request.success, request); - - if ((typeof xhr.onCompleteFn) == 'function') { - xhr.onCompleteFn(xhr.jsdo, request.success, request); - } - - } - }; - - /* - * Accepts changes for all table references in the JSDO. - */ - this.acceptChanges = function () { - for (var buf in this._buffers) { - this._buffers[this._buffers[buf]._name].acceptChanges(); - } - }; - - /* - * Rejects changes for the table references in the JSDO. - */ - this.rejectChanges = function () { - for (var buf in this._buffers) { - this._buffers[this._buffers[buf]._name].rejectChanges(); - } - }; - - /* - * Returns an array with changes for all table references in the JSDO. - */ - this.getChanges = function () { - var result = []; - for (var buf in this._buffers) { - var changes = this._buffers[this._buffers[buf]._name].getChanges(); - result = result.concat(changes); - } - return result; - }; - - this.hasChanges = function () { - for (var buf in this._buffers) { - if (this._buffers[this._buffers[buf]._name].hasChanges()) - return true; - } - return false; - }; - - /* - * Private method to apply changes for all table references in the JSDO. - * If _errorString has been set for a row, rejectRowChanges() is called. - * If it has not been set, acceptRowChanges() is called. - */ - this._applyChanges = function () { - for (var buf in this._buffers) { - this._buffers[this._buffers[buf]._name]._applyChanges(); - } - }; - - /* - * Accepts row changes for the working record using the JSDO reference. - */ - this.acceptRowChanges = function () { - if (this._defaultTableRef) - return this._defaultTableRef.acceptRowChanges(); - throw new Error(msg.getMsgText("jsdoMSG001", "acceptRowChanges()")); - }; - - /* - * Reject row changes for the working record using the JSDO reference. - */ - this.rejectRowChanges = function () { - if (this._defaultTableRef) - return this._defaultTableRef.rejectRowChanges(); - throw new Error(msg.getMsgText("jsdoMSG001", "rejectRowChanges()")); - }; - - /* - * Sets complete set of properties for the jsdo. All existing properties are replaced with new set - */ - this.setProperties = function( propertiesObject ) { - var prop; - - if (arguments.length < 1) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'setProperties', 1)); - } - if (arguments.length > 1) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'setProperties', 1)); - } - if ( typeof propertiesObject == "object" ) { - /* Copy properties of the propertiesObject argument into _properties. - * Note that if object passed in has a prototype, this code copies them too) - */ - this._properties = {}; - - for (prop in propertiesObject) { - if( propertiesObject.hasOwnProperty(prop) ) { - if (typeof propertiesObject[prop] !== "function" ) { - this._properties[prop] = propertiesObject[prop]; - } - } - } - } - else if ( (propertiesObject === undefined) || (propertiesObject === null) ) { - this._properties = {}; - } - else { - // {1}: Parameter {1} must be of type {3} in {4} call. - throw new Error(progress.data._getMsgText("jsdoMSG121", 'JSDO', 1, 'Object', - 'setProperties')); - } - }; - - /* - * Set or remove an individual property in the property set maintained by the jsdo. - * This operates only on the property identified by propertyName; - * all other existing properties remain as they are. - * If the propertyName is not part of the context, this call adds it. - * If it exists, it is updated, unless - - * If propertyValue is undefined, this call removes the property - */ - this.setProperty = function( propertyName, propertyValue) { - if (arguments.length < 2) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', - 'setProperty', 2)); - } - if (arguments.length !== 2) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", "JSDO", - "setProperty", 2)); - } - if (typeof propertyName !== "string") { - // {1}: Parameter {1} must be of type {3} in {4} call. - throw new Error(progress.data._getMsgText("jsdoMSG121", 'JSDO', 1, 'string', - 'setProperty')); - } - - if ( propertyValue === undefined ) { - delete this._properties[propertyName]; // OK if it doesn't exist -- no error - } - else { - this._properties[propertyName] = propertyValue; - } - }; - - /* - * Gets the set of jsdo properties. Returns an object containing all the properties - */ - this.getProperties = function( ) { - if (arguments.length > 0) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperties', 0)); - } - return this._properties; - }; - - /* Gets the value of an individual property in the jsdo property set - */ - this.getProperty = function( propertyName) { - if (arguments.length < 1) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperty', 1)); - } - if (arguments.length > 1) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'JSDO', 'getProperty', 1)); - } - return this._properties[propertyName]; - - }; - - /////////////////////////////////////////////////////////////////////////// - // - // The following methods provide support for Object Pesistence - - /* - * Saves JSDO memory (and optionally pending changes) to local storage. - * - * saveLocal() - * saveLocal(name) - * saveLocal(dataMode) - * saveLocal(name, dataMode) - * - */ - this.saveLocal = function saveLocal(arg1, arg2) { - var name; - var dataMode; - - if (arguments.length > 2) { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); - } - - if (typeof(arg1) == 'string' || arg1 === null || arg1 === undefined) { - name = arg1; - dataMode = arg2; - } - else { - name = null; - dataMode = arg1; - } - - if (name === undefined || name === null || name === "") { - name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; - } - if (typeof(dataMode) == 'undefined') { - dataMode = progress.data.JSDO.ALL_DATA; - } - else { - switch (dataMode) { - case progress.data.JSDO.ALL_DATA: - case progress.data.JSDO.CHANGES_ONLY: - break; - default: - throw new Error(msg.getMsgText("jsdoMSG115", arguments.callee.name)); - } - } - - if (this._localStorage === null) { - // Must first instantiate _localStorage object - this._localStorage = new progress.data.LocalStorage(); - } - - var dataObj = this._prepareDataObjectForLocalStorage(dataMode); - this._localStorage.saveToLocalStorage(name, dataObj); - }; - - /* - * Reads localStorage (based upon name) into JSDO memory - * (localStorage may or may not have pending changes). - * readLocal() - * readLocal(name) - * - */ - this.readLocal = function readLocal(name) { - if (arguments.length > 1) { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); - } - if (name === undefined || name === null || name === "") { - name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; - } - else if (typeof(name) != 'string') { - throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); - } - - if (this._localStorage === null) { - this._localStorage = new progress.data.LocalStorage(); - } - - var object = this._localStorage.readFromLocalStorage(name); - - // If storage area does not exist (i.e. object = null) then don't update JSDO local memory - if (object) { - if (this._hasMatchingSchema(object) === false) - throw new Error(msg.getMsgText("jsdoMSG117", name)); - - // For readLocal(), JSDO should first be emptied of data, so using MODE_EMPTY - this._restoreFromLocalStorage(object, progress.data.JSDO.MODE_EMPTY); - } - - return object !== null; - }; - - /* - * Reads localStorage (based upon name) into JSDO memory - * (localStorage may or may not have pending changes). - * addLocalRecords(addMode) - * addLocalRecords(addMode, keyFields) - * addLocalRecords(name, addMode) - * addLocalRecords(name, addMode, keyFields) - */ - this.addLocalRecords = function addLocalRecords(arg1, arg2, arg3) { - var name; - var addMode; - var keyFields; - - if (arguments.length < 1) { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); - } - - if (typeof(arg1) == 'string') { - name = arg1; - addMode = arg2; - keyFields = arg3; - } - else { - name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; - addMode = arg1; - keyFields = arg2; - } - - if (typeof(name) == 'undefined' || name === null || name === "") { - name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; - } - else if (typeof(name) != 'string') { - throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); - } - - if (addMode != progress.data.JSDO.MODE_REPLACE) { - throw new Error(msg.getMsgText("jsdoMSG115", arguments.callee.name)); - } - - if (this._localStorage === null) { - this._localStorage = new progress.data.LocalStorage(); - } - - var object = this._localStorage.readFromLocalStorage(name); - - // If storage area does not exist (i.e. object = null) then don't update JSDO local memory - if (object) { - if (this._hasMatchingSchema(object) === false) - throw new Error(msg.getMsgText("jsdoMSG117", name)); - - try { - this._restoreFromLocalStorage(object, addMode, keyFields); - } - catch (e) { - var text = e.message; - throw new Error(text.replace(new RegExp('addRecords', 'g'), 'addLocalRecords')); - } - } - - return object !== null; - }; - - - /* - * This method returns True if each buffer in the jsdo contains a primary key. - */ - this._containsPrimaryKeys = function _containsPrimaryKeys() { - - for (var buf in this._buffers) { - if (this._buffers[buf]._primaryKeys === null) - return false; - } - - return true; - }; - - /* - * Compares JSDO's dataset/table names with those in specified storage object. - * Returns true if they match (or if storageObject is null or empty), else false. - */ - this._hasMatchingSchema = function _hasMatchingSchema(storageObject) { - var isValid = true; - - if (storageObject === null || (Object.keys(storageObject).length === 0)) - return true; - - - if (this._dataSetName) { - if (storageObject[this._dataSetName]) { - for (var buf in this._buffers) - if (storageObject[this._dataSetName][buf] === undefined) { - isValid = false; - break; - } - } - else - isValid = false; // dataset should be in storage area - } - else if (this._dataProperty) { - // If array, we had to wrap in "fake" dataset, so unwrap it - storageObject = storageObject["_localStorage"]; - if (storageObject === undefined || storageObject[this._dataProperty] === undefined) - isValid = false; - } - else { - // If temp-table, we had to wrap in "fake" dataset, so unwrap it - storageObject = storageObject["_localStorage"]; - if (storageObject === undefined || storageObject[this._defaultTableRef._name] === undefined) - isValid = false; - } - - return isValid; - }; - - - /* - * Clears the data saved to local storage. - * - * deleteLocal() - * deleteLocal(name) - */ - this.deleteLocal = function deleteLocal(name) { - if (arguments.length > 1) { - throw new Error(msg.getMsgText("jsdoMSG024", "JSDO", arguments.callee.name + "()")); - } - if (name === undefined || name === null || name === "") { - name = "jsdo_" + this._resource.service.name + "_" + this._resource.name; - } - else if (typeof(name) != 'string') { - throw new Error(msg.getMsgText("jsdoMSG116", "name", arguments.callee.name + "()")); - } - - if (this._localStorage === null) { - this._localStorage = new progress.data.LocalStorage(); - } - this._localStorage.clearLocalStorage(name); - }; - - - // This method is used by saveLocal() to return a jsonObject with current JSDO data based upon option. - // - // In order to take advantage of existing code (createChangeSet() and addRecords()) and particularly - // to use the processing of before-data in addRecords(), for tables and arrays, we create a dummy - // dataset name: _localStorage. - this._prepareDataObjectForLocalStorage = function (option) { - - var storageObject = {}; - - // DataSets - if (this._dataSetName) { - switch (option) { - case progress.data.JSDO.ALL_DATA: - storageObject = this._createDataAndChangeSet(this._dataSetName); - break; - - case progress.data.JSDO.CHANGES_ONLY: - storageObject = this._createChangeSet(this._dataSetName, true); - break; - } - } - // Arrays - else if (this._dataProperty) { - switch (option) { - case progress.data.JSDO.ALL_DATA: - storageObject = this._createDataAndChangeSet("_localStorage"); - break; - - case progress.data.JSDO.CHANGES_ONLY: - storageObject = this._createChangeSet("_localStorage", true); - break; - } - } - // Temp Tables - else { - switch (option) { - case progress.data.JSDO.ALL_DATA: - storageObject = this._createDataAndChangeSet("_localStorage"); - break; - - case progress.data.JSDO.CHANGES_ONLY: - storageObject = this._createChangeSet("_localStorage", true); - break; - } - } - - return storageObject; - }; - - - // Restore the data retrieved from local storage to the JSDO based upon the specified addMode - this._restoreFromLocalStorage = function (storageObject, addMode, keyFields) { - - if (storageObject && (Object.keys(storageObject).length > 0)) { - if (this._dataSetName) { - // Walk thru all tables to retrieve data - for (var buf in this._buffers) - this._restoreDataForTable(this._buffers[buf], storageObject, addMode, keyFields); - } - // Either temp-table or array - else - this._restoreDataForTable(this._defaultTableRef, storageObject, addMode, keyFields); - } - else if (addMode === progress.data.JSDO.MODE_EMPTY) - this._clearData(); - }; - - - this._restoreDataForTable = function (tableRef, jsonObject, addMode, keyFields) { - - // If primaryKeys not found, check if the idProperty is there - keyFields = keyFields !== undefined ? keyFields : tableRef._primaryKeys; - if (keyFields === undefined && this._resource.idProperty) { - keyFields = []; - keyFields[0] = this._resource.idProperty; - } - - if (this._dataSetName) { - var oldUseRelationships = this.useRelationships; - // Turn off useRelationships since addRecords() does not use the working record - this.useRelationships = false; - - try { - tableRef.addRecords(jsonObject, addMode, keyFields); - } finally { - // Restore useRelationships - this.useRelationships = oldUseRelationships; - } - } - // else it's either an array (this._dataProperty) or a temp-table - else { - // Creating dummy dataset name: "_localStorage" for tables and arrays - this._dataSetName = "_localStorage"; - tableRef.addRecords(jsonObject, addMode, keyFields); - this._dataSetName = null; - } - }; - - this.getMethodProperties = function(operation, name) { - var idx; - - if (this._resource._operations) { - if (this._resource._operations[operation]) { - return this._resource._operations[operation]; - } - } - else { - this._resource._operations = {}; - } - for (var idx = 0; idx < this._resource.operations.length; idx++) { - if (this._resource.operations[idx].type == operation) { - return (this._resource._operations[operation] = this._resource.operations[idx]); - } - } - }; - - /////////////////////////////////////////////////////////////////////////// - - // Load data - if (autoFill) - this.fill(); - - }; // End of JSDO - - // Constants for progress.data.JSDO - if ((typeof Object.defineProperty) == 'function') { - Object.defineProperty(progress.data.JSDO, 'MODE_APPEND', { - value: 1, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'MODE_EMPTY', { - value: 2, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'MODE_MERGE', { - value: 3, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'MODE_REPLACE', { - value: 4, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'ERROR', { - value: -1, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'APP_ERROR', { - value: -2, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'RETVAL', { - value: -3, - enumerable: true - }); - Object.defineProperty(progress.data.JSDO, 'DATA_ERROR', { - value: -4, - enumerable: true - }); - } else { - progress.data.JSDO.MODE_APPEND = 1; - progress.data.JSDO.MODE_EMPTY = 2; - progress.data.JSDO.MODE_MERGE = 3; - progress.data.JSDO.MODE_REPLACE = 4; - } - - /* CRUD */ - progress.data.JSDO._OP_CREATE = 1; - progress.data.JSDO._OP_READ = 2; - progress.data.JSDO._OP_UPDATE = 3; - progress.data.JSDO._OP_DELETE = 4; - progress.data.JSDO._OP_SUBMIT = 5; - - /* Offline support: saving data to local storage */ - progress.data.JSDO.ALL_DATA = 1; - progress.data.JSDO.CHANGES_ONLY = 2; - - // Arrays elements as individual fields - // Separator must have at least one characters - progress.data.JSDO.ARRAY_INDEX_SEPARATOR = "_"; - -// setup inheritance for JSDO - progress.data.JSDO.prototype = new progress.util.Observable(); - progress.data.JSDO.prototype.constructor = progress.data.JSDO; - progress.data.JSDO.prototype.toString = function (radix) { - return "JSDO"; - }; - -// setup inheritance for table reference - progress.data.JSTableRef.prototype = new progress.util.Observable(); - progress.data.JSTableRef.prototype.constructor = progress.data.JSTableRef; - progress.data.JSTableRef.prototype.toString = function (radix) { - return "JSTableRef"; - }; - - // Built-in Plugins - progress.data.PluginManager.addPlugin("JFP", { - requestMapping: function(jsdo, params, info) { - var sortFields, - field, - fieldName, - fieldInfo, - tableName, - filter, - sortDir, - ablFilter, - sqlQuery, - methodProperties, - capabilities, - index, - position, - option, - capabilitiesObject, - reqCapabilities = { - filter: { options: [ "ablFilter", "sqlQuery" ], mapping: undefined }, - top: { options: [ "top" ], mapping: undefined }, - skip: { options: [ "skip" ], mapping: undefined }, - id: { options: [ "id" ], mapping: undefined }, - sort: { options: [ "orderBy" ], mapping: undefined } - }, - doConversion = true, - param; - - if (info.operation === "read") { - capabilitiesObject = {}; - methodProperties = jsdo.getMethodProperties(info.operation); - capabilities = methodProperties.capabilities; - - if (capabilities) { - capabilities = capabilities.replace(/\s/g, "").split(","); - for (index = 0; index < capabilities.length; index += 1) { - capabilitiesObject[capabilities[index]] = true; - } - } - for (param in params) { - if (param && (params[param] !== undefined) && reqCapabilities[param]) { - for (index = 0; index < reqCapabilities[param].options.length; index += 1) { - option = reqCapabilities[param].options[index]; - if (capabilitiesObject[option]) { - reqCapabilities[param].mapping = option; - break; - } - } - if (!reqCapabilities[param].mapping) { - throw new Error(msg.getMsgText("jsdoMSG120", - reqCapabilities[param].options.join("' or '"), param)); - } - } - } - - if (jsdo._defaultTableRef && params.tableRef === undefined) { - tableName = jsdo._defaultTableRef._name; - } - else { - tableName = params.tableRef; - } - - if (params.sort) { - // Convert sort expression to JFP format - - if (typeof(params.sort) === "object" && !(params.sort instanceof Array)) { - // Kendo UI sort format - object - // Make params.sort an array - params.sort = [params.sort]; - } - sortFields = ""; - for (index = 0; index < params.sort.length; index += 1) { - field = params.sort[index]; - sortDir = ""; - - if (typeof(field) === "string") { - // setSortFields format - // Extract fieldName and sortDir from string - fieldName = field; - position = field.indexOf(":"); - if (position !== -1) { - sortDir = fieldName.substring(position + 1); - fieldName = fieldName.substring(0, position); - switch(sortDir.toLowerCase()) { - case "desc": - case "descending": - sortDir = "desc"; - break; - } - } - } else { - // Kendo UI sort format - array - // Extract fieldName and sortDir from object - fieldName = field.field; - if (params.sort[index].dir === "desc") { - sortDir = params.sort[index].dir; - } - } - if (tableName) { - // Use original fieldName instead of serialized name - fieldInfo = jsdo[tableName]._fields[fieldName.toLowerCase()]; - if (fieldInfo && fieldInfo.origName) { - fieldName = fieldInfo.origName; - } - } - if (sortDir === "desc") { - fieldName += " DESC"; - } - sortFields += fieldName; - if (index < params.sort.length - 1) { - sortFields += ","; - } - } - } - - if (params.filter) { - // If filter is specified as string, then no conversion is necessary - if (typeof params.filter === 'string') { - doConversion = false; - } - - params.tableRef = tableName; - - if (doConversion && (params.tableRef === undefined)) { - throw new Error(msg.getMsgText("jsdoMSG045", "fill() or read()", "params", - "tableRef")); - } - - if (reqCapabilities["filter"].mapping === "ablFilter") { - if (doConversion) { - ablFilter = progress.util._convertToABLWhereString( - jsdo._buffers[params.tableRef], params.filter); - } - else { - ablFilter = params.filter; - } - } - else if (reqCapabilities["filter"].mapping === "sqlQuery") { - if (doConversion) { - sqlQuery = progress.util._convertToSQLQueryString( - jsdo._buffers[params.tableRef], params.filter, true); - } - else { - sqlQuery = params.filter; - } - } - } - - filter = JSON.stringify({ - ablFilter: ablFilter, - sqlQuery: sqlQuery, - orderBy: sortFields, - skip: params.skip, - top: params.top - }); - - params = {filter: filter}; - } - return params; - } - }); - - if (typeof progress.ui == 'undefined') - progress.ui = {}; - progress.ui.UITableRef = function UITableRef(tableRef) { - this._tableRef = tableRef; - this._listview = null; - this._detailPage = null; - this._listviewContent = undefined; - - this.addItem = function (format) { - var detailForm; - - if (!this._tableRef.record) - throw new Error(msg.getMsgText("jsdoMSG002", this._name)); - - if (!this._listview) return; - - format = format ? format : this._listview.format; - detailForm = (this._detailPage && this._detailPage.name) ? this._detailPage.name : ""; - - if (this._listviewContent === undefined) { - this.clearItems(); - } - var text = this._listview.itemTemplate ? - this._listview.itemTemplate : progress.ui.UIHelper._itemTemplate; - - text = text.replace(new RegExp('{__format__}', 'g'), format); - text = text.replace(new RegExp('{__id__}', 'g'), this._tableRef.record.data._id); - text = text.replace(new RegExp('{__page__}', 'g'), detailForm); - - for (var field in this._tableRef.record.data) { - var value = this._tableRef.record.data[field]; - text = text.replace(new RegExp('{' + field + '}', 'g'), - (value !== undefined && value !== null) ? value : ""); - } - - this._listviewContent += text; - }; - - this.clearItems = function () { - if (this._listview) { - this._listviewContent = ''; - var listviewElement = document.getElementById(this._listview.name); - if (listviewElement) { - listviewElement.innerHTML = ''; - } - } - }; - - this._getFormFieldValue = function (fieldName, detailPageName) { - var value = null; - - if (detailPageName === undefined) { - if (this._detailPage && this._detailPage.name) - detailPageName = this._detailPage.name; - } - - if (typeof($) == 'function' && detailPageName) { - field = $("#" + detailPageName + " #" + fieldName); - if (!field || field.length === 0) - field = $("#" + detailPageName + ' [dsid="' + fieldName + '"]'); - if (field && field.length == 1) - value = field.val(); - } - else { - field = document.getElementById(fieldName); - if (field) { - value = field.value; - } - } - - return value; - }; - - this._setFormField = function (fieldName, value, detailPageName) { - var field = null; - - if (detailPageName === undefined) { - if (this._detailPage && this._detailPage.name) - detailPageName = this._detailPage.name; - } - - if (typeof($) == 'function' && detailPageName) { - field = $("#" + detailPageName + " #" + fieldName); - if (!field || field.length === 0) - field = $("#" + detailPageName + ' [dsid="' + fieldName + '"]'); - if (field && field.length == 1) - field.val(value); - } - else { - field = document.getElementById(fieldName); - if (field) { - field.value = value; - } - } - }; - - /* - * Assigns field values from the form. - */ - this.assign = function (detailPageName) { - if (!this._tableRef.record) - throw new Error(msg.getMsgText("jsdoMSG002", this._tableRef._name)); - if ((arguments.length !== 0) && (typeof detailPageName != 'string')) - throw new Error(msg.getMsgText("jsdoMSG024", "UIHelper", "assign()")); - - // Ensure creation of before image record - this._tableRef.record.assign(null); - - var fieldName; - var schema = this._tableRef.getSchema(); - for (var i = 0; i < schema.length; i++) { - fieldName = schema[i].name; - if (fieldName == '_id') continue; - var value = this._getFormFieldValue(fieldName, detailPageName); - // CR OE00241289 Should always copy over field value unless undefined, - // user may have explicitly set it to blank - if (typeof value != 'undefined') { - if (typeof value == 'string' && schema[i].type != 'string') { - value = this._tableRef._jsdo._convertType(value, - schema[i].type, - schema[i].items ? schema[i].items.type : null); - } - this._tableRef.record.data[fieldName] = value; - } - } - - // Ensure order of record - this._tableRef.record._sortRecord(); - - return true; - }; - - this.display = function (pageName) { - if (!this._tableRef.record) - throw new Error(msg.getMsgText("jsdoMSG002", this._tableRef._name)); - - // Display record to form - var schema = this._tableRef.getSchema(); - for (var i = 0; i < schema.length; i++) { - this._setFormField(schema[i].name, this._tableRef.record.data[schema[i].name], pageName); - } - this._setFormField('_id', this._tableRef.record.data._id, pageName); - }; - - this.showListView = function () { - if (!this._listview) return; - - var uiTableRef = this; - var listviewElement; - if (typeof($) == 'function') { - listviewElement = $("#" + this._listview.name); - if (listviewElement && listviewElement.length == 1) { - listviewElement.html(this._listviewContent ? this._listviewContent : ''); - try { - if (listviewElement.attr("data-filter") === "true" - && typeof listviewElement.filterable === "function") { - listviewElement.filterable("refresh"); - } - else { - listviewElement.listview("refresh"); - } - } - catch (e) { - // Workaround for issue with JQuery Mobile throwning exception on refresh - } - } - - if (this._listview.autoLink) { - // Add trigger for 'tap' event to items - $("#" + this._listview.name + " li").each( - function (/* index */) { - $(this).bind('click', - function (/* event, ui */) { - var jsrecord = uiTableRef.getListViewRecord(this); - uiTableRef.display(); - if (typeof(uiTableRef._listview.onSelect) == 'function') { - uiTableRef._listview.onSelect(event, this, jsrecord); - } - }); - }); - } - } - else { - listviewElement = document.getElementById(this._listview.name); - if (listviewElement) { - listviewElement.innerHTML = this._listviewContent; - } - - if (this._listview.autoLink) { - var element = document.getElementById(this._listview.name); - if (element && element.childElementCount > 0) { - for (var i = 0; i < element.children.length; i++) { - element.children[i].onclick = function () { - var jsrecord = uihelper.getListViewRecord(this); - uihelper.display(); - if (typeof(uiTableRef._listview.onSelect) == 'function') { - uiTableRef._listview.onSelect(event, this, jsrecord); - } - }; - } - } - } - } - - this._listviewContent = undefined; - }; - - this.getFormFields = function (fields) { - if (!this._tableRef._schema) - return ''; - if (!(fields instanceof Array)) - fields = null; - else { - var tmpFields = {}; - for (var i = 0; i < fields.length; i++) { - tmpFields[fields[i]] = fields[i]; - } - fields = tmpFields; - } - var htmltext; - if (!fields || fields['_id']) { - htmltext = ''; - } - else - htmltext = ''; - htmltext += '
'; - - for (var i = 0; i < this._tableRef._schema.length; i++) { - var fieldName = this._tableRef._schema[i].name; - if (fieldName == '_id') continue; - if (fieldName.length > 0 && fieldName.charAt(0) == '_') continue; - if (fields && fields[fieldName] === undefined) continue; - var fieldLabel = this._tableRef._schema[i].title ? - this._tableRef._schema[i].title : this._tableRef._schema[i].name; - var text = (this._detailPage && this._detailPage.fieldTemplate) ? - this._detailPage.fieldTemplate : progress.ui.UIHelper._fieldTemplate; - text = text.replace(new RegExp('{__label__}', 'g'), fieldLabel); - text = text.replace(new RegExp('{__name__}', 'g'), this._tableRef._schema[i].name); - htmltext += text; - } - htmltext += '
'; - fields = null; - return htmltext; - }; - - this.getListViewRecord = function (htmlIElement) { - var id = htmlIElement.getAttribute('data-id'); - return this._tableRef.findById(id); - }; - - this.getFormRecord = function (detailPageName) { - var id = this._getFormFieldValue('_id', detailPageName); - return this._tableRef.findById(id); - }; - - this._getIdOfElement = function (name) { - if (typeof($) == 'function') { - var element = $("#" + name); - if (!element || element.length === 0) { - element = $('[dsid="' + name + '"]'); - if (element && element.length == 1) { - var id = element.attr("id"); - if (id) - return id; - } - } - } - return name; - }; - - this.setDetailPage = function setDetailPage(obj) { - if (!obj || (typeof(obj) != 'object')) - throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "object")); - if (!obj.name || (typeof(obj.name) != 'string')) - throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "name")); - this._detailPage = obj; - this._detailPage.name = this._getIdOfElement(this._detailPage.name); - }; - this.setListView = function setListView(obj) { - if (!obj || (typeof(obj) != 'object')) - throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "object")); - if (!obj.name || (typeof(obj.name) != 'string')) - throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "name")); - if (obj.format && (typeof(obj.name) != 'string')) - throw new Error(msg.getMsgText("jsdoMSG012", arguments.callee.name, "format")); - - this._listview = obj; - this._listview.name = this._getIdOfElement(this._listview.name); - if (!this._listview.format) { - if (typeof($) == 'function') { - for (var i = 0; i < this._tableRef._schema.length; i++) { - var fieldName = this._tableRef._schema[i].name; - - field = $("#" + this._listview.name + ' [dsid="' + fieldName + '"]'); - if (field && field.length == 1) { - field.html('{' + fieldName + '}'); - } - } - } - var text = document.getElementById(this._listview.name).innerHTML; - var pos = text.indexOf('
'; - progress.ui.UIHelper._defaultFieldTemplate = '
' + - '' + - '
'; - progress.ui.UIHelper._itemTemplate = progress.ui.UIHelper._defaultItemTemplate; - progress.ui.UIHelper._fieldTemplate = progress.ui.UIHelper._defaultFieldTemplate; - - progress.ui.UIHelper.setItemTemplate = function (template) { - progress.ui.UIHelper._itemTemplate = template ? template : progress.ui.UIHelper._defaultItemTemplate; - }; - - progress.ui.UIHelper.setFieldTemplate = function (template) { - progress.ui.UIHelper._fieldTemplate = - template ? template : progress.ui.UIHelper._defaultFieldTemplate; - }; - -})(); - -//this is so that we can see the code in Chrome's Source tab when script is loaded via XHR -//# sourceURL=progress.jsdo.js -/* -progress.session.js Version: 4.4.1-2 - -Copyright (c) 2012-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - /* define these if not defined yet - they may already be defined if - progress.js was included first */ - if (typeof progress === "undefined") { - progress = {}; - } - if (typeof progress.data === "undefined") { - progress.data = {}; - } - - progress.data.ServicesManager = {}; - progress.data.ServicesManager._services = []; - progress.data.ServicesManager._resources = []; - progress.data.ServicesManager._data = []; - progress.data.ServicesManager._sessions = []; - progress.data.ServicesManager._jsdosessions = []; - /* - progress.data.ServicesManager.put = function(id, jsdo) { - progress.data.ServicesManager._data[id] = jsdo; - }; - progress.data.ServicesManager.get = function(id) { - return progress.data.ServicesManager._data[id]; - }; - */ - - progress.data.ServicesManager.addResource = function (id, resource) { - if (progress.data.ServicesManager._resources[id] === undefined) - progress.data.ServicesManager._resources[id] = resource; - else - throw new Error("A resource named '" + id + "' was already loaded."); - }; - progress.data.ServicesManager.getResource = function (id) { - return progress.data.ServicesManager._resources[id]; - }; - progress.data.ServicesManager.addService = function (id, service) { - if (progress.data.ServicesManager._services[id] === undefined) - progress.data.ServicesManager._services[id] = service; - else - throw new Error("A service named '" + id + "' was already loaded."); - }; - progress.data.ServicesManager.getService = function (id) { - return progress.data.ServicesManager._services[id]; - }; - progress.data.ServicesManager.addSession = function (catalogURI, session) { - if (progress.data.ServicesManager._sessions[catalogURI] === undefined) - progress.data.ServicesManager._sessions[catalogURI] = session; - else - throw new Error("Cannot load catalog '" + catalogURI + "' multiple times."); - }; - - progress.data.ServicesManager.addJSDOSession = function (catalogURI, jsdosession) { - if (progress.data.ServicesManager._jsdosessions[catalogURI] === undefined) { - progress.data.ServicesManager._jsdosessions[catalogURI] = jsdosession; - } - else { - throw new Error("Cannot load catalog '" + catalogURI + "' multiple times."); - } - }; - progress.data.ServicesManager.getSession = function (catalogURI) { - try { - return progress.data.ServicesManager._sessions[catalogURI]; - } - catch (e) { - return null; - } - }; - - progress.data.ServicesManager.cleanSession = function (session) { - var servicesKey, - resourcesKey, - sessionsKey, - service, - services = progress.data.ServicesManager._services, - resources = progress.data.ServicesManager._resources, - sessions = progress.data.ServicesManager._sessions, - jsdosessions = progress.data.ServicesManager._jsdosessions; - - // Delete the services and resources in the ServicesManager - // associated with the Session given - for (servicesKey in services) { - service = null; - if (services[servicesKey]._session === session) { - service = services[servicesKey]; - delete services[servicesKey]; - } - - if (!service) { - continue; - } - - for (resourcesKey in resources) { - if (resources[resourcesKey].service === service) { - delete resources[resourcesKey]; - } - } - } - - // Delete the session and jsdosession from the ServicesManager - for (sessionsKey in sessions) { - if (sessions[sessionsKey] === session) { - delete sessions[sessionsKey]; - - if(jsdosessions[sessionsKey]) { - delete jsdosessions[sessionsKey]; - } - } - } - }; - - /* - * Scans URL for parameters of the form {name} - * Returns array with the names - */ - function extractParamsFromURL(url) { - var urlParams = []; - if (typeof(url) == 'string') { - var paramName = null; - for (var i = 0; i < url.length; i++) { - if (url.charAt(i) == '{') { - paramName = ""; - } - else if (url.charAt(i) == '}') { - if (paramName) - urlParams.push(paramName); - paramName = null; - } - else if (paramName !== null) { - paramName += url.charAt(i); - } - } - } - return urlParams; - } - - /* - * Adds the catalog.json file provided by the catalog parameter, which is a JSDO - * that has loaded the catalog - */ - progress.data.ServicesManager.addCatalog = function (services, session) { - if (!services) { - throw new Error("Cannot find 'services' property in catalog file."); - } - if (services instanceof Array) { - - // first check if there are duplicates before we add them to our cache, - // which only handles unique values - for (var j = 0; j < services.length; j++) { - // don't allow services with the same name across sessions - if (progress.data.ServicesManager.getService(services[j].name) !== undefined) - throw new Error("A service named '" + services[j].name + "' was already loaded."); - - var resources = services[j].resources; - - if (resources instanceof Array) { - for (var i = 0; i < resources.length; i++) { - if (progress.data.ServicesManager.getResource(resources[i].name) !== undefined) - throw new Error("A resource named '" + resources[i].name + - "' was already loaded."); - } - } - else { - throw new Error("Missing 'resources' array in catalog."); - } - } - - for (var j = 0; j < services.length; j++) { - services[j]._session = session; - this.addService(services[j].name, services[j]); // Register the service - var resources = services[j].resources; - var baseAddress = services[j].address; - if (resources instanceof Array) { - for (var i = 0; i < resources.length; i++) { - var resource = resources[i]; - resource.fn = {}; - resource.service = services[j]; - resources[i].url = baseAddress + resources[i].path; - // Register resource - progress.data.ServicesManager.addResource(resources[i].name, resources[i]); - - // Process schema - resource.fields = null; - resource.primaryKeys = null; - if (resource.schema) { - resource.fields = {}; - resource.primaryKeys = {}; - resource._dataSetName = undefined; - resource._tempTableName = undefined; - var properties = null; - - try { - if (typeof resource.schema.properties != 'undefined') { - var keys = Object.keys(resource.schema.properties); - properties = resource.schema.properties; - if (keys.length == 1) { - if (typeof resource.schema.properties[keys[0]].properties != - 'undefined') { - // Schema corresponds to a DataSet - resource._dataSetName = keys[0]; - } - else if (typeof resource.schema.properties[keys[0]].items != - 'undefined') { - // Schema corresponds to a temp-table - resource.dataProperty = keys[0]; - properties = resource.schema.properties[keys[0]].items.properties; - resource._tempTableName = resource.dataProperty; - resource.primaryKeys[resource._tempTableName] = - resource.schema.properties[keys[0]].primaryKey; - } - } - } - else { - var keys = Object.keys(resource.schema); - if (keys.length == 1) { - resource.dataProperty = keys[0]; - if (typeof resource.schema[keys[0]].items != 'undefined') { - // Catalog format correspond to Table Schema - properties = resource.schema[keys[0]].items.properties; - resource._tempTableName = resource.dataProperty; - resource.primaryKeys[resource._tempTableName] = - resource.schema[keys[0]].primaryKey; - } - else if (typeof resource.schema[keys[0]].properties != 'undefined') { - // Catalog format correspond to DataSet Schema - resource._dataSetName = keys[0]; - resource.dataProperty = null; - properties = resource.schema; - } - } - } - } - catch (e) { - throw new Error("Error parsing catalog file."); - } - if (properties) { - if (resource._dataSetName) { - properties = properties[resource._dataSetName].properties; - for (var tableName in properties) { - resource.fields[tableName] = []; - resource.primaryKeys[tableName] = properties[tableName].primaryKey; - var tableProperties; - if (properties[tableName].items - && properties[tableName].items.properties) { - tableProperties = properties[tableName].items.properties; - } - else { - tableProperties = properties[tableName].properties; - } - for (var field in tableProperties) { - tableProperties[field].name = field; - if (field != '_id') - resource.fields[tableName].push(tableProperties[field]); - } - } - } - else { - var tableName = resource.dataProperty ? resource.dataProperty : ""; - resource.fields[tableName] = []; - for (var field in properties) { - properties[field].name = field; - if (field != '_id') - resource.fields[tableName].push(properties[field]); - } - } - } - else - throw new Error("Error parsing catalog file."); - } - else - resource.fields = null; - - // Validate relationship property - if ((resource.relations instanceof Array) - && resource.relations[0] - && resource.relations[0].RelationName) { - throw new Error( - "Relationship properties in catalog must begin with lowercase."); - } - // Process operations - resource.generic = {}; - if (resource.operations) { - for (var idx = 0; idx < resource.operations.length; idx++) { - if (resource.operations[idx].path) { - resource.operations[idx].url = - resource.url + resource.operations[idx].path; - } - else { - resource.operations[idx].url = resource.url; - } - if (!resource.operations[idx].params) { - resource.operations[idx].params = []; - } - if (!resource.operations[idx].type) { - resource.operations[idx].type = "INVOKE"; - } - - // Set opname - validation of opname is done later - var opname = resource.operations[idx].type.toLowerCase(); - - // Set default verb based on operation - if (!resource.operations[idx].verb) { - switch (opname) { - case 'create': - resource.operations[idx].verb = "POST"; - break; - case 'read': - resource.operations[idx].verb = "GET"; - break; - case 'update': - case 'invoke': - case 'submit': - case 'count': - resource.operations[idx].verb = "PUT"; - break; - case 'delete': - resource.operations[idx].verb = "DELETE"; - break; - default: - break; - } - } - - // Point fn to operations - var func = function fn(object, async) { - var deferred; - - // Add static variable fnName to function - if (typeof fn.fnName == 'undefined') { - fn.fnName = arguments[0]; // Name of function - fn.definition = arguments[1]; // Operation definition - return; - } - - var reqBody = null; - var url = fn.definition.url; - var jsdo = this; - var xhr = null; - - var request = {}; - if (object) { - if (typeof(object) != "object") { - throw new Error("Catalog error: Function '" + - fn.fnName + "' requires an object as a parameter."); - } - var objParam; - if (object instanceof XMLHttpRequest - || (object.constructor - && object.constructor.name === "XMLHttpRequest")) { - jsdo = object.jsdo; - xhr = object; - objParam = xhr.objParam; - - // use the request from the xhr request if possible - request = xhr.request; - } - else { - objParam = object; - } - - if (typeof async == 'undefined') { - async = this._async; - } - else { - async = Boolean(async); - } - - request.objParam = objParam; - - - // Process objParam - var isInvoke = (fn.definition.type.toUpperCase() == 'INVOKE'); - for (var i = 0; i < fn.definition.params.length; i++) { - var name = fn.definition.params[i].name; - switch (fn.definition.params[i].type) { - case 'PATH': - case 'QUERY': - case 'MATRIX': - var value = null; - if (objParam) - value = objParam[name]; - if (!value) - value = ""; - if (url.indexOf('{' + name + '}') == -1) { - throw new Error("Catalog error: Reference to " + - fn.definition.params[i].type + " parameter '" + - name + "' is missing in path."); - } - url = url.replace( - new RegExp('{' + name + '}', 'g'), - encodeURIComponent(value)); - break; - case 'REQUEST_BODY': - case 'REQUEST_BODY,RESPONSE_BODY': - case 'RESPONSE_BODY,REQUEST_BODY': - if (xhr && !reqBody) { - reqBody = objParam; - } - else { - var reqParam = objParam[name]; - if (isInvoke - && (fn.definition.params[i].xType - && ("DATASET,TABLE".indexOf( - fn.definition.params[i].xType) != -1))) { - var unwrapped = (jsdo._resource.service.settings - && jsdo._resource.service.settings.unwrapped); - if (unwrapped) { - // Remove extra level if found - if ((typeof(reqParam) == 'object') - && (Object.keys(reqParam).length == 1) - && (typeof(reqParam[name]) == 'object')) - reqParam = reqParam[name]; - } - else { - // Add extra level if not found - if ((typeof(reqParam) == 'object') - && (typeof(reqParam[name])=='undefined')){ - reqParam = {}; - reqParam[name] = objParam[name]; - } - } - } - if (!reqBody) { - reqBody = {}; - } - reqBody[name] = reqParam; - } - break; - case 'RESPONSE_BODY': - break; - default: - throw new Error("Catalog error: " + - "Unexpected parameter type '" + - fn.definition.params[i].type + "'."); - } - } - - // URL has parameters - if (url.indexOf('{') != -1) { - var paramsFromURL = extractParamsFromURL(url); - for (var i = 0; i < paramsFromURL.length; i++) { - var name = paramsFromURL[i]; - var value = null; - if (objParam) - value = objParam[name]; - if (!value) - value = ""; - if (typeof(value) === "object") { - value = JSON.stringify(value); - } - url = url.replace( - new RegExp('{' + name + '}', 'g'), - encodeURIComponent(value)); - } - } - } - - request.fnName = fn.fnName; - request.async = async; - - if (request.deferred === undefined && - typeof($) == 'function' && typeof($.Deferred) == 'function') { - deferred = $.Deferred(); - request.deferred = deferred; - } - - var data = jsdo._httpRequest(xhr, fn.definition.verb, - url, reqBody, request, async); - return data; - }; - // End of Function Definition - - switch (resource.operations[idx].verb.toLowerCase()) { - case 'get': - case 'post': - case 'put': - case 'delete': - break; - default: - throw new Error("Catalog error: Unexpected HTTP verb '" + - resource.operations[idx].verb + - "' found while parsing the catalog."); - } - - switch (opname) { - case 'invoke': - break; - case 'create': - case 'read': - case 'update': - case 'delete': - case 'submit': - case 'count': - if (typeof(resource.generic[opname]) == "function") { - throw new Error("Catalog error: Multiple '" + - resource.operations[idx].type + - "' operations specified in the catalog for resource '" + - resource.name + "'."); - } - else - resource.generic[opname] = func; - break; - default: - throw new Error("Catalog error: Unexpected operation '" + - resource.operations[idx].type + - "' found while parsing the catalog."); - } - - // Set fnName - var name = resource.operations[idx].name; - if (opname === "invoke" || opname === "count") { - resource.fn[name] = {}; - resource.fn[name]["function"] = func; - } - else { - name = "_" + opname; - } - func(name, resource.operations[idx]); - } - } - } - } - } - } - else { - throw new Error("Missing 'services' array in catalog."); - } - - }; - - /* - * Prints debug information about the ServicesManager. - */ - progress.data.ServicesManager.printDebugInfo = function (resourceName) { - if (resourceName) { - //console.log("** ServicesManager **"); - //console.log("** BEGIN **"); - var resource = progress.data.ServicesManager.getResource(resourceName); - if (resource) { - var cSchema = "Schema:\n"; - var cOperations = "Operations: " + resource.operations.length + "\n"; - for (var field in resource.schema.properties) { - cSchema += "\nName: " + field - + "\n"; - } - - for (var i = 0; i < resource.operations.length; i++) { - cOperations += "\n" + i - + "\nName: " + resource.operations[i].name - + "\nURL: " + resource.operations[i].url - + "\ntype: " + resource.operations[i].type - + "\nverb: " + resource.operations[i].verb - + "\nparams: " + resource.operations[i].params.length - + "\n"; - } - console.log("** DEBUG INFO **\nResource name: %s\nURL:%s\n%s\n%s\n\n", - resource.name, resource.url, cSchema, cOperations); - } - else - console.log("Resource not found"); - //console.log("** END **"); - } - }; - - - /* - * Contains information about a server-side Mobile service. - * Properties of args parameter for constructor: - * @param name the name of the service - * @param uri the URI of the service - */ - progress.data.MobileServiceObject = function MobileServiceObject(args) { - var _name = args.name; - Object.defineProperty(this, 'name', - { - get: function () { - return _name; - }, - enumerable: true - }); - - var _uri = args.uri; - Object.defineProperty(this, 'uri', - { - get: function () { - return _uri; - }, - enumerable: true - }); - }; - - /* - An object that maintains the X-CLIENT-PROPS header string - The data for the string is stored in the internal variable named contextObject and is - always up to date. The internal var contextString isn't created until the first time it's - needed (the first get of the contextHeader property), and then it's updated an cached - A call to setContext or setContextProperty updates contextObject but sets contextString to - null, which signals that it needs to be updated. If contextObject is an empty object, - contextString is set to undefined to indicate that no header is to be sent - */ - progress.data.ContextProperties = function() { - var contextObject = {}, - contextString; // if null, contextObject has been changed but string wasn't updated yet - - // the string to be sent in the X-CLIENT-PROPS header (unless Session.xClientProps has been set) - Object.defineProperty(this, 'contextHeader', - { - get: function () { - var header; - - if (contextString === null) { // needs to be updated - header = JSON.stringify( contextObject ); - if (header === "{}") { - contextString = undefined; - } - else { - contextString = header; - } - } - // else (contextString === undefined || has a usable value) - - return contextString; - }, - enumerable: true - }); - - /* determine whether the property is already present, and - - add it if it's not present - remove it if propertyValue is explicitly passed as undefined - otherwise replace its value (even if the new value is null or "") - */ - this.setContextProperty = function( propertyName, propertyValue) { - if (arguments.length < 2) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', - 'setContextProperty', 2)); - } - if (arguments.length !== 2) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", "Session", - "setContextProperty", 2)); - } - if (typeof propertyName !== "string") { - // {1}: Parameter {1} must be of type {3} in {4} call. - throw new Error(progress.data._getMsgText("jsdoMSG121", 'Session', 1, 'string', - 'setContextProperty')); - } - - if ( propertyValue === undefined ) { - delete contextObject[propertyName]; // OK if it doesn't exist -- no error - } - else { - contextObject[propertyName] = propertyValue; - } - contextString = null; // must be updated on next get of this.contextHeader - }; - - this.setContext = function( context ) { - var prop; - - if (arguments.length < 1) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'setContext', 1)); - } - if (arguments.length > 1) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'setContext', 1)); - } - if ( typeof context == "object" ) { - /* Copy the properties of the context passed in as an argument into - * an internal contextObject. (Note that if the context object passed in - * has a prototype, this code copies them, too) - */ - contextObject = {}; - for (prop in context) { - if( context.hasOwnProperty(prop) ) { - if (typeof context[prop] !== "function" ) { - contextObject[prop] = context[prop]; - } - } - } - } - else if ( (context === undefined) || (context === null) ) { - contextObject = {}; - } - else { - // {1}: Parameter {1} must be of type {3} in {4} call. - throw new Error(progress.data._getMsgText("jsdoMSG121", 'Session', 1, 'Object', - 'setContextProperty')); - } - contextString = null; // must be updated on next get of this.contextHeader - }; - - this.getContext = function( ) { - if (arguments.length > 0) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContext', 0)); - } - return contextObject; - }; - - this.getContextProperty = function( propertyName) { - if (arguments.length < 1) { - // {1}: Incorrect number of arguments in {2} call. There should be {3}. - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContextProperty', 1)); - } - if (arguments.length > 1) { - // {1}: Incorrect number of arguments in {2} call. There should be only {3}."; - throw new Error(progress.data._getMsgText("jsdoMSG122", 'Session', 'getContextProperty', 1)); - } - return contextObject[propertyName]; - }; - - }; // end of ContextProperties - - /* - * Manages authentication and session ID information for a service. - * - * Use: OE mobile developer instantiates a session and calls addCatalog() to load - * information for one or more services defined in a catalog file. - * - * Developer instantiates JDSOs as needed. - * Usually all of the JSDOs will use the same session, but if a client-side - * service needs resources from more than one REST app, there would need to be more - * than one session - * - */ - progress.data.Session = function Session(options) { - - var defPropSupported = false; - if ((typeof Object.defineProperty) == 'function') { - defPropSupported = true; - } - - var that = this, - jsdosession, // "backpointer" if this Session is being used by a JSDOSession - isUserAgentiOS = false, // checked just below this var statement - isFirefox = false, // checked just below this var statement - isEdge = false, // checked just below this var statement - isIE = false, // checked just below this var statement - canPassCredentialsToOpenWithCORS = false, // False will always work if creds are correct - defaultiOSBasicAuthTimeout = 4000, - deviceIsOnline = true, // online until proven offline - restApplicationIsOnline = false, // was the Mobile Web Application that this Session object - // connects to online the last time it was checked? - // (value is always false if session is not logged in) - oepingAvailable = false, - defaultPartialPingURI = "/rest/_oeping", - partialPingURI = defaultPartialPingURI, - _storageKey, - _authProvider = null, - customCredentials = false, - - // Note: the variables above here are used during the lifetime of the object; the ones below - // are only used while the constructor is executing - storedAuthModel, - storedURI, - newURI, - stateWasReadFromStorage = false; - - // This is a hidden argument to suppress this warning and be re-used for future warnings - if (!options || options._silent !== true) { - console.warn("Session: As of JSDO 4.4, the Session object has been deprecated. Please use the JSDOSession object instead."); - } - - if (typeof navigator !== "undefined") { - if (typeof navigator.userAgent !== "undefined") { - isUserAgentiOS = navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)/i); - isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; - // detect that we're running in MS Edge browser - isEdge = navigator.userAgent.indexOf('Edge/') > -1; - // detect that we're running in IE 11 (or IE 11 in pre-11 mode) or IE 10 browser - isIE = ( (navigator.userAgent.indexOf('Trident/')) > -1 || (navigator.userAgent.indexOf('MSIE 10') > -1)); - } - } - - // Firefox, Edge, and IE will throw an error on the send() if CORS is being used for the request - // and we have included credentials in the URI (which is what passing them to open() does), - canPassCredentialsToOpenWithCORS = !(isFirefox || isEdge || isIE); - - // When using basic authentication, we can pass the user name and password to the XMLHttpRequest.open() - // method. However, in some browsers, passing credentials to open() will result in the xhr's .send() - // method throwing an error. The goal of this function is to figure out whether it's safe to include - // the credentials. It returns false if there could be a problem, true otherwise. - // Note: currently it does this solely on the basis of what browser we are running in, regardless - // of whether the request will actually use the CORS protocol. Ideally, we should take into account whether - // the request will actually require CORS. The question is whether we can reliably do that. - // The reason for taking the specific request into account is that there are drawbacks to not passing the - // credentials when we are NOT using CORS, namely that if the credentials are invalid, some browsers will - // put up their own prompt for credentials in non-CORS situations (those browsers are IE, Edge, and Chrome) - function canPassCredentialsToOpen() { - return canPassCredentialsToOpenWithCORS; - } - - this._onlineHandler = function () { - setDeviceIsOnline(true); - that.trigger("online", that, null); - }; - - this._offlineHandler = function () { - setDeviceIsOnline(false); - that.trigger("offline", that, progress.data.Session.DEVICE_OFFLINE, null); - }; - - if ((typeof window != 'undefined' ) && (window.addEventListener)) { - window.addEventListener("online", this._onlineHandler, false); - window.addEventListener("offline", this._offlineHandler, false); - } - - /* constants and properties - define them as properties via the defineProperty() - * function, which has "writable" and "configurable" parameters that both - * default to false, so these calls create properties that are read-only - * - * IF WE DECIDE THAT WE CAN ASSUME WE ALWAYS RUN WITH A VERSION OF JAVASCRIPT THAT SUPPORTS - * Object.DefineProperty(), WE CAN DELETE THE defPropSupported VARIABLE, THE TEST OF IT BELOW, - * AND THE 'ELSE' CLAUSE BELOW AND ALL THE setXxxx functions (AND CHANGE THE CALLS TO THE setXxxx - * FUNCTIONS SO THEY JUST REFER TO THE PROPERTY) - * - */ - - // define these unconditionally so we don't get a warning on the push calls that they might - // have been uninitialized - var _catalogURIs = []; - var _services = []; - var _jsdos = []; - - this.onOpenRequest = null; - - var _password = null; - - if (defPropSupported) { - var _userName = null; - Object.defineProperty(this, 'userName', - { - get: function () { - return _userName; - }, - enumerable: true - }); - - var _loginTarget = '/static/home.html'; - Object.defineProperty(this, 'loginTarget', - { - get: function () { - return _loginTarget; - }, - enumerable: true - }); - - var _serviceURI = null; - Object.defineProperty(this, 'serviceURI', - { - get: function () { - return _serviceURI; - }, - enumerable: true - }); - - Object.defineProperty(this, 'catalogURIs', - { - get: function () { - return _catalogURIs; - }, - enumerable: true - }); - - Object.defineProperty(this, 'services', - { - get: function () { - return _services; - }, - enumerable: true - }); - - var _loginResult = null; - Object.defineProperty(this, 'loginResult', - { - get: function () { - return _loginResult; - }, - enumerable: true - }); - - var _loginHttpStatus = null; - Object.defineProperty(this, 'loginHttpStatus', - { - get: function () { - return _loginHttpStatus; - }, - enumerable: true - }); - - var _clientContextId = null; - Object.defineProperty(this, 'clientContextId', - { - get: function () { - return _clientContextId; - }, - enumerable: true - }); - - var _authenticationModel = progress.data.Session.AUTH_TYPE_ANON; - Object.defineProperty(this, 'authenticationModel', - { - get: function () { - return _authenticationModel; - }, - set: function (newval) { - if (newval) { - newval = newval.toLowerCase(); - } - switch (newval) { - case progress.data.Session.AUTH_TYPE_FORM : - case progress.data.Session.AUTH_TYPE_BASIC : - case progress.data.Session.AUTH_TYPE_ANON : - case progress.data.Session.AUTH_TYPE_SSO : - case null : - _authenticationModel = newval; - storeSessionInfo("authenticationModel", newval); - break; - default: - throw new Error("Error setting Session.authenticationModel. '" + - newval + "' is an invalid value."); - } - }, - enumerable: true - }); - - var _lastSessionXHR = null; - Object.defineProperty(this, 'lastSessionXHR', - { - get: function () { - return _lastSessionXHR; - }, - enumerable: true - }); - - Object.defineProperty(this, 'connected', - { - get: function () { - return (this.loginResult === progress.data.Session.LOGIN_SUCCESS) - && restApplicationIsOnline - && deviceIsOnline; - }, - enumerable: true - }); - - Object.defineProperty(this, 'JSDOs', - { - get: function () { - return _jsdos; - }, - enumerable: true - }); - - var _pingInterval = 0; - var _timeoutID = null; - Object.defineProperty(this, 'pingInterval', - { - get: function () { - return _pingInterval; - }, - set: function (newval) { - if ( (typeof newval === "number") && (newval >= 0) ) { - _pingInterval = newval; - storeSessionInfo("pingInterval", newval); - if (newval > 0) { - // if we're logged in, start autopinging - if (this.loginResult === progress.data.Session.LOGIN_SUCCESS) { - _timeoutID = setTimeout(this._autoping, newval); - } - } - else if (newval === 0) { - clearTimeout(_timeoutID); - _pingInterval = 0; - } - } - else { - throw new Error("Error setting Session.pingInterval. '" + - newval + "' is an invalid value."); - } - }, - enumerable: true - }); - - var _contextProperties = new progress.data.ContextProperties(); - Object.defineProperty( this, - "_contextProperties", - { - get: function () { - return _contextProperties; - }, - enumerable: false - } - ); - - var isInvalidated = false; - Object.defineProperty( - this, - "_isInvalidated", - { - get: function () { - return isInvalidated; - }, - enumerable: false - } - ); - - // used internally, not supported as part of the Session API (tho authProvider is part - // of the *JSDOSession* API) - Object.defineProperty( this, - "_authProvider", - { - get: function () { - return _authProvider; - }, - set: function(newval) { - if (_authProvider) { - throw new Error("Internal Error setting Session._authProvider. '" + - "The property has already been set."); - - } else { - setAuthProvider(newval); - } - }, - enumerable: false - } - ); - } - else { - this.userName = null; - this.loginTarget = '/static/home.html'; - this.serviceURI = null; - this.catalogURIs = []; - this.services = []; - this.loginResult = null; - this.loginHttpStatus = null; - this.clientContextId = null; - this.authenticationModel = progress.data.Session.AUTH_TYPE_ANON; - this.lastSessionXHR = null; - } - - // stores data value using the JSDOSession's storage key plus the infoName - // argument as a key. If there is no infoName, just uses the storage key - // by itself (the latter case is intended to serve as a flag that we have - // stored this JSDOSession's data before) - // - function storeSessionInfo(infoName, value) { - var key; - if (that.loginResult === progress.data.Session.LOGIN_SUCCESS && - typeof (sessionStorage) === 'object' && _storageKey) { - - key = _storageKey; - if (infoName) { - key = key + "." + infoName; - } - if (typeof (value) !== 'undefined') { - sessionStorage.setItem(key, JSON.stringify(value)); - } - } - } - - function retrieveSessionInfo(infoName) { - var key, - jsonStr, - value = null; - if (typeof (sessionStorage) === 'object' && _storageKey) { - key = _storageKey; - if (infoName) { - key = key + "." + infoName; - } - jsonStr = sessionStorage.getItem(key); - if (jsonStr !== null) { - try { - value = JSON.parse(jsonStr); - } catch (e) { - value = null; - } - } - return value; - } - } - - function clearSessionInfo(infoName) { - var key; - if (typeof (sessionStorage) === 'object' && _storageKey) { - key = _storageKey; - if (infoName) { - key = key + "." + infoName; - sessionStorage.removeItem(key); - } - } - } - - function storeAllSessionInfo() { - if (_storageKey) { - storeSessionInfo("loginResult", that.loginResult); - storeSessionInfo("userName", that.userName); - storeSessionInfo("serviceURI", that.serviceURI); - storeSessionInfo("loginHttpStatus", that.loginHttpStatus); - storeSessionInfo("authenticationModel", that.authenticationModel); - storeSessionInfo("pingInterval", that.pingInterval); - storeSessionInfo("oepingAvailable", oepingAvailable); - storeSessionInfo("partialPingURI", partialPingURI); - storeSessionInfo("clientContextId", that.clientContextId); - storeSessionInfo("deviceIsOnline", deviceIsOnline); - storeSessionInfo("restApplicationIsOnline", restApplicationIsOnline); - if (that._authProvider) { - storeSessionInfo("_authProvider.init", - {uri: that._authProvider.uri, - authenticationModel: that._authProvider.authenticationModel}); - } - storeSessionInfo(_storageKey, true); - } - } - - function clearAllSessionInfo() { - if (_storageKey) { - if (retrieveSessionInfo(_storageKey)) { - clearSessionInfo("loginResult"); - clearSessionInfo("userName"); - clearSessionInfo("serviceURI"); - clearSessionInfo("loginHttpStatus"); - clearSessionInfo("clientContextId"); - clearSessionInfo("deviceIsOnline"); - clearSessionInfo("restApplicationIsOnline"); - clearSessionInfo("authenticationModel"); - clearSessionInfo("pingInterval"); - clearSessionInfo("oepingAvailable"); - clearSessionInfo("partialPingURI"); - clearSessionInfo("_authProvider.init"); - clearSessionInfo(_storageKey); - } - } - } - - function setSessionInfoFromStorage(key) { - var authproviderInitObject, - authProvider; - if (retrieveSessionInfo(key)) { - setLoginResult(retrieveSessionInfo("loginResult"), this); - setUserName(retrieveSessionInfo("userName"), this); - setServiceURI(retrieveSessionInfo("serviceURI"), this); - setLoginHttpStatus(retrieveSessionInfo("loginHttpStatus"), this); - setClientContextID(retrieveSessionInfo("clientContextId"), this); - setDeviceIsOnline(retrieveSessionInfo("deviceIsOnline")); - setRestApplicationIsOnline(retrieveSessionInfo("restApplicationIsOnline")); - that.authenticationModel = retrieveSessionInfo("authenticationModel"); - that.pingInterval = retrieveSessionInfo("pingInterval"); - setOepingAvailable(retrieveSessionInfo("oepingAvailable")); - setPartialPingURI(retrieveSessionInfo("partialPingURI")); - // if information on an AuthenticationProvider for the session is in storage, and if - // the authProvider hasn't already been set for this Session, create a new authProvider - // using the same info as the old one. This would be likely to happen if the app's code - // had used the old JSDOSession.login API, where we create the AuthenticationProvider - // automatically during login instead of the code passing one to the constructor - if (!that._authProvider) { - authproviderInitObject = retrieveSessionInfo("_authProvider.init"); - if (authproviderInitObject) { - setAuthProvider(new progress.data.AuthenticationProvider(authproviderInitObject)); - } - } - } - } - - function setUserName(newname, sessionObject) { - if (defPropSupported) { - _userName = newname; - } - else { - sessionObject.userName = newname; - } - - storeSessionInfo("userName", newname); - } - - function setLoginTarget(target, sessionObject) { - if (defPropSupported) { - _loginTarget = target; - } - else { - sessionObject.loginTarget = target; - } - } - - function setServiceURI(url, sessionObject) { - if (defPropSupported) { - _serviceURI = url; - } - else { - sessionObject.serviceURI = url; - } - - storeSessionInfo("serviceURI", url); - } - - function pushCatalogURIs(url, sessionObject) { - if (defPropSupported) { - _catalogURIs.push(url); - } - else { - sessionObject.catalogURIs.push(url); - } - } - - function pushService(serviceObject, sessionObject) { - if (defPropSupported) { - _services.push(serviceObject); - } - else { - sessionObject.services.push(serviceObject); - } - } - - function findService(serviceName) { - for (var prop in _services) { - var srv = _services[prop]; - if (srv.name === serviceName) { - return srv; - } - } - return null; - } - - function setLoginResult(result, sessionObject) { - if (defPropSupported) { - _loginResult = result; - } else { - sessionObject.loginResult = result; - } - - if (result === progress.data.Session.LOGIN_SUCCESS) { - storeSessionInfo("loginResult", result); - } else { - // Let's clear sessionStorage since we logged out or something went bad! - clearAllSessionInfo(); - } - } - - function setLoginHttpStatus(status, sessionObject) { - if (defPropSupported) { - _loginHttpStatus = status; - } - else { - sessionObject.loginHttpStatus = status; - } - - storeSessionInfo("loginHttpStatus", status); - } - - function setClientContextIDfromXHR(xhr, sessionObject) { - if (xhr) { - setClientContextID(getResponseHeaderNoError(xhr, "X-CLIENT-CONTEXT-ID"), sessionObject); - } - } - - function setClientContextID(ccid, sessionObject) { - if (defPropSupported) { - _clientContextId = ccid; - } - else { - sessionObject.clientContextId = ccid; - } - - storeSessionInfo("clientContextId", ccid); - } - - function setLastSessionXHR(xhr, sessionObject) { - if (defPropSupported) { - _lastSessionXHR = xhr; - } - else { - sessionObject.lastSessionXHR = xhr; - } - } - - function setDeviceIsOnline(value) { - deviceIsOnline = value; - - storeSessionInfo("deviceIsOnline", value); - } - - function setAuthProvider(value) { - // Do this to preserve authprovider's null-ness. - _authProvider = value ? value : null; - } - - function setRestApplicationIsOnline(value) { - restApplicationIsOnline = value; - - storeSessionInfo("restApplicationIsOnline", value); - } - - function setOepingAvailable(value) { - oepingAvailable = value; - - storeSessionInfo("oepingAvailable", value); - } - - function setPartialPingURI(value) { - partialPingURI = value; - - storeSessionInfo("partialPingURI", value); - } - - /* - When using CORS, if the client asks for a response header that is not among - the headers exposed by the Web application, the user agent may write an error - to the console, e.g., "REFUSED TO GET UNSAFE HEADER". This function checks for - a given response header in a way that will avoid the error message. It does this - by requesting all headers and then checking to see whether the desired header - is present (it will not be present, even if the server sent it, if the server has not - also allowed that header). The function caches the string returned by getAllResponseHeaders - by storing it on the xhr that was used in the request. It does the caching in - case there is another header to be checked. - */ - function getResponseHeaderNoError(xhr, headerName) { - var allHeaders = xhr._pdsResponseHeaders, - regExp; - - if (allHeaders === undefined) { - allHeaders = xhr.getAllResponseHeaders(); - if ( allHeaders ) { - xhr._pdsResponseHeaders = allHeaders; - } - else { - xhr._pdsResponseHeaders = null; - } - } - if ( allHeaders ) { - regExp = new RegExp("^" + headerName + ":", "m"); - if ( allHeaders.match(regExp) ) { - return xhr.getResponseHeader(headerName); - } - } - - return null; - } - - // "Methods" - - this._pushJSDOs = function (jsdo) { - _jsdos.push(jsdo); - }; - - - /* _openRequest (intended for progress.data library use only) - * calls open() for an xhr -- the assumption is that this is an xhr for a JSDO, and we need to add - * some session management information for the request, such as user credentials and a session ID if - * there is one - * - * The callback parameter is to support async calls --- it's possible that the call in here to - * _openRequestAndAuthorize will make an async request (for token refresh), so it's expected that - * callers will invoke _openRequest with a callback parameter for async execution - */ - this._openRequest = function (xhr, verb, url, async, callback) { - var urlPlusCCID, - that = this; - - function afterOpenAndAuthorize(xhr) { - // add CCID header - if (that.clientContextId && (that.clientContextId !== "0")) { - xhr.setRequestHeader("X-CLIENT-CONTEXT-ID", that.clientContextId); - } - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(that, xhr); - - if (typeof that.onOpenRequest === 'function') { - var params = { - "xhr": xhr, - "verb": verb, - "uri": urlPlusCCID, - "async": async, - "formPreTest": false, - "session": that - }; - that.onOpenRequest(params); - // xhr = params.xhr; //Note that, currently, this would have no effect in the caller. - } - if (callback) { - callback(); - } - } - - if (this._isInvalidated) { - // Session: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "Session")); - } - - if (this.loginResult !== progress.data.Session.LOGIN_SUCCESS && !this._authProvider && this.authenticationModel) { - throw new Error("Attempted to make server request when there is no active session."); - } - - // if resource url is not absolute, add the REST app url to the front - urlPlusCCID = this._prependAppURL(url); - - // add CCID as JSESSIONID query string to url - urlPlusCCID = this._addCCIDtoURL(urlPlusCCID); - - // add time stamp to the url - if (progress.data.Session._useTimeStamp) { - urlPlusCCID = progress.data.Session._addTimeStampToURL(urlPlusCCID); - } - - // should be able to remove this check and only do what's in the "if" when we no longer - // support calling the Session API directly (need to keep that now because tdriver, for - // one, uses the Session object, and uses it synchronously - if (this._authProvider) { - this._authProvider._openRequestAndAuthorize(xhr, - verb, - urlPlusCCID, - async, - afterOpenAndAuthorize); - } else { - this._setXHRCredentials(xhr, verb, urlPlusCCID, this.userName, _password, async); - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - _addWithCredentialsAndAccept(xhr, "application/json"); - } - afterOpenAndAuthorize(xhr); - } - - }; - - // callback used in login to determine whether ping is available on server - this.pingTestCallback = function (cbArgs) { - var foundOeping = cbArgs.pingResult ? true : false; - - setOepingAvailable(foundOeping); - }; - - // generic async callback, currently used by login(), addCatalog(), logout(), connect, and disconnect - this._onReadyStateChangeGeneric = function () { - var xhr = this; - var result; - var errorObject; - - clearTimeout(xhr._requestTimeout); // for the iOS Basic Auth bug - - if (xhr.readyState == 4) { - result = null; - errorObject = null; - - // initial processing of the response from the Web application - if ((typeof xhr.onResponseFn) == 'function') { - try { - result = xhr.onResponseFn(xhr); - // ( note that result will remain null if this is a logout() ) - } - catch (e) { - errorObject = e; - } - } - // handle the results of the processing (e.g., fire any events required) - if ((typeof xhr.onResponseProcessedFn) == 'function') { - if (!result) { - result = progress.data.Session.GENERAL_FAILURE; - } - xhr.onResponseProcessedFn(xhr.pdsession, result, errorObject, xhr); - } - } - }; - - // Intended only for internal use by the JSDO library - // NOTE: disconnect does not currently send a request to the Web application for the Anonymous or - // OE SSO models. It's conceivable, though unlikely, that it might. For that reason, the design is - // similar to the functions that DO make a server request. There is a "setup" function (this one) - // and a separate function to process the "result" (_processDisconnectResult, below). Currently the - // setup function is minimal and just calls _processDisconnectResult directly. If we ever do need to - // send a server request, _processDisconnectResult will be specified as the callback to be invoked - // from onReadyStateChangeGeneric. The possibility of this potential enhancement is the reason for - // the odd signature of _processDisconnectResult, which has a currently unused first parameter for - // the potential XHR. - this._disconnect = function (deferred) { - - // Note: we use the "no harm, no foul" approach for disconnect. If you aren't connected, it's - // regarded as a success rather than cause for throwing an error. - this._processDisconnectResult(null, deferred); - }; - - - // This is separate from _disconnect for cases in which _disconnect makes a server request. - // If there has been a server request, xhr should be valid and deferred will be undefined - // If there was no server request, xhr will be undefined and deferred will be valid. - // If this needs to be enhanced to support server requests, see _procesLoginResponse as - // a general model - // Probably the only time this function will be called as the result of a server request is with - // Form authentication, and even then it's questionable - this._processDisconnectResult = function (xhr, deferred) { - - this._reinitializeAfterLogout(this, progress.data.Session.SUCCESS); - this._disconnectComplete(this, progress.data.Session.SUCCESS, null, null, deferred); - }; - - this._disconnectComplete = function (pdsession, result, errObj, xhr, deferred) { - pdsession.trigger("afterDisconnect", pdsession, result, errObj, xhr, deferred); - }; - - - // GET RID OF progress.data.Session login CODE (AND RELATED) IF WE DROP SUPPORT FOR USING - // THE OLD progress.data.Session API DIRECTLY (mainly a problem for existing code (tdriver), - // or anyone who wants to call methods synchronously, which should be no one) - /* login - * - */ - - // store password here until successful login; only then do we store it in the Session object - var pwSave = null; - // store user name here until successful login; only then do we store it in the Session object - var unameSave = null; - this.login = function (serviceURI, loginUserName, loginPassword, loginTarget) { - var uname, - pw, - isAsync = false, - args = [], - deferred, - iOSBasicAuthTimeout, - uriForRequest; // "decorated" version of serviceURI, used to actually send the request - - pwSave = null; // in case these are left over from a previous login - unameSave = null; - - if (!defPropSupported) { - // this is here on the presumably slim chance that we're running with a - // version of JavaScript that doesn't support defineProperty (otherwise - // the lower casing will have already happened). When we decide that it's - // OK to remove our conditionalization of property definitions, we should - // get rid of this whole conditional - this.authenticationModel = this.authenticationModel.toLowerCase(); - } - - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // Session: Cannot call login() when authenticationModel is SSO. - // Please use the AuthenticationProvider object instead. - throw new Error(progress.data._getMsgText("jsdoMSG057", 'Session', 'login()')); - } - - if (this.loginResult === progress.data.Session.LOGIN_SUCCESS || this._authProvider) { - throw new Error("Attempted to call login() on a Session object that is already logged in."); - } - - if (arguments.length > 0) { - if (arguments[0] && typeof(arguments[0]) === 'object') { - // Note that arguments[0].serviceURI may be undefined because when the JSDOSession - // uses a Session internally, it passes serviceURI to the constructor. The other - // properties may be present, though - args[0] = arguments[0].serviceURI; - args[1] = arguments[0].userName; - args[2] = arguments[0].password; - args[3] = arguments[0].loginTarget; - args[4] = arguments[0].async; - - /* Special for JSDOSession: if this method was called by a JSDOSession object, - it passes deferred and jsdosession and we need to eventually attach them - to the XHR we use so that the promise created by the JSDOSession will work - correctly - */ - deferred = arguments[0].deferred; - - iOSBasicAuthTimeout = arguments[0].iOSBasicAuthTimeout; - if ( typeof iOSBasicAuthTimeout === 'undefined' ) { - iOSBasicAuthTimeout = defaultiOSBasicAuthTimeout; - } - else if (iOSBasicAuthTimeout && (typeof iOSBasicAuthTimeout != 'number')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'login', - 'The iOSBasicAuthTimeout argument was invalid.')); - } - } - else { - args = arguments; - } - } - - if (args.length > 0) { - if (args[0]) { - var restURLtemp = args[0]; - - // get rid of trailing '/' because appending service url that starts with '/' - // will cause request failures - if (restURLtemp[restURLtemp.length - 1] === "/") { - restURLtemp = restURLtemp.substring(0, restURLtemp.length - 1); - } - setServiceURI(restURLtemp, this); - } else if (!this.serviceURI) { - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); - throw new Error("Session.login() is missing the serviceURI argument."); - } - - if (args[1]) { - uname = args[1]; - } - - if (args[2]) { - pw = args[2]; - } - - if (args[3]) { - setLoginTarget(args[3], this); - } - - if (args[4]) { - if (typeof(args[4]) === 'boolean') { - isAsync = args[4]; - } - else { - throw new Error("Session.login() was passed an async setting that is not a boolean."); - } - } - } - else { - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); - throw new Error("Session.login() is missing the serviceURI argument."); - } - - // use these temp cred variables later; if login succeeds, we'll use them to set the - // real credentials - unameSave = uname; - pwSave = pw; - - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_ANON || - this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - /* anonymous should NOT have a username and password passed (this is - probably unnecessary because the XHR seems to send the request without - credentials first, then intercept the 401 if there is one and try again, - this time with credentials. Just making sure. - */ - /* For form authentication, we may as well not send the user name and password - * on this request, since we are just trying to test whether the authentication - * has already happened and they are therefore irrelevant - */ - uname = null; - pw = null; - } - - var xhr = new XMLHttpRequest(); - xhr.pdsession = this; - - try { - uriForRequest = this.serviceURI + this.loginTarget; - if (progress.data.Session._useTimeStamp) { - uriForRequest = progress.data.Session._addTimeStampToURL(uriForRequest); - } - this._setXHRCredentials(xhr, 'GET', uriForRequest, uname, pw, isAsync); - - progress.data.Session._setNoCacheHeaders(xhr); - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(this, xhr); - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - _addWithCredentialsAndAccept(xhr, - "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - } - - xhr._isAsync = isAsync; - if (isAsync) { - xhr.onreadystatechange = this._onReadyStateChangeGeneric; - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - xhr.onResponseFn = this._afterFormPretestLogin; - } - else { - xhr.onResponseFn = this._processLoginResult; - xhr.onResponseProcessedFn = this._loginComplete; - } - if ( this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC - && isUserAgentiOS - && iOSBasicAuthTimeout > 0 ) { - xhr._requestTimeout = setTimeout( function (){ - clearTimeout(xhr._requestTimeout); - xhr._iosTimeOutExpired = true; - xhr.abort(); - }, - iOSBasicAuthTimeout); - } - xhr._jsdosession = jsdosession; // in case the caller is a JSDOSession - xhr._deferred = deferred; // in case the caller is a JSDOSession - } - - if (typeof this.onOpenRequest === 'function') { - var isFormPreTest = false; - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - isFormPreTest = true; - } - - // set this here in case onOpenRequest checks it - setLastSessionXHR(xhr, this); - var params = { - "xhr": xhr, - "verb": "GET", - "uri": this.serviceURI + this.loginTarget, - "async": false, - "formPreTest": isFormPreTest, - "session": this - }; - this.onOpenRequest(params); - xhr = params.xhr; // just in case it has been changed - } - setLastSessionXHR(xhr, this); - xhr.send(null); - } - catch (e) { - clearTimeout(xhr._requestTimeout); - setLoginHttpStatus(xhr.status, this); - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, this); - unameSave = null; - pwSave = null; - throw e; - } - - if (isAsync) { - return progress.data.Session.ASYNC_PENDING; - } - else { - setLoginHttpStatus(xhr.status, this); - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - return (this._afterFormPretestLogin(xhr) ); - } - else { - return (this._processLoginResult(xhr) ); - } - } - }; - - - this._afterFormPretestLogin = function (xhr) { - var pdsession = xhr.pdsession; - setLoginHttpStatus(xhr.status, xhr.pdsession); - - var formLoginParams = { - "xhr": xhr, - "pw": pwSave, - "uname": unameSave, - "theSession": pdsession - }; - try { - return doFormLogin(formLoginParams); - } - catch (e) { - pwSave = null; - unameSave = null; - throw e; - } - }; - - /* doFormLogin - * This function handles logging in to a service that uses form-based authentication. It's separate - * from the main login function because it's long. One of the things it does is examine the - * response from an initial attempt to get the login target without credentials (done in the main - * login() function) to determine whether the user has already been authenticated. Although a - * current OE Mobile Web application (as of 5/30/2013) will return an error if authentication - * failed on a form login, previous versions and non-OE servers return a - * redirect to a login page and the user agent (browser or native wrapper) - * usually then fetches the redirect location and returns it along with a - * 200 Success status, when in fcat it was an authentication failure. Hence - * the need to analyze the response to try to figure out what we get back. - * - */ - function doFormLogin(args) { - var xhr = args.xhr; - var theSession = args.theSession; - var oldXHR; - - // check whether we got the OE REST Form based error response - var contentType = null; - var needAuth = false; - var params = { - "session": theSession, - "xhr": xhr, - "statusFromjson": null - }; - - contentType = xhr.getResponseHeader("Content-Type"); - - if (contentType && contentType.indexOf("application/json") >= 0) { - handleJSONLoginResponse(params); - if ( !params.statusFromjson - || (params.statusFromjson >= 400 && params.statusFromjson < 500) - ) { - needAuth = true; - } - else { - // either the response shows that we're already authenticated, or - // there's some error other than an authentication error - setLoginHttpStatus(params.statusFromjson, theSession); - } - } - else { - // need to do only 200 for async to work with MWA down - if (theSession.loginHttpStatus == 200) { - if (_gotLoginForm(xhr)) { - needAuth = true; - } - // else we are assuming we truly retrieved the login target and - // therefore we were previously authenticated - } - // else had an error, just return it - } - - if (needAuth) { - // create new XHR, because if this is an async call we don't want to - // confuse things by using this xhr to send another request while we're - // still processing its old request (this function, doFormLogin(), may - // have been called from onReadyStateChangeGeneric and it's conceivable - // that that function has more code to execute involving this xhr) - oldXHR = xhr; - xhr = new XMLHttpRequest(); - args.xhr = xhr; - params.xhr = xhr; - - // need to transfer any properties that the Session code stored in the - // the xhr that need to persist across the 2 requests made by a our - // login implementation for Form auth - xhr.pdsession = oldXHR.pdsession; - xhr._isAsync = oldXHR._isAsync; - xhr._deferred = oldXHR._deferred; // special for JSDOSession - xhr._jsdosession = oldXHR._jsdosession; // special for JSDOSession - - xhr.open('POST', theSession.serviceURI + "/static/auth/j_spring_security_check",xhr._isAsync); - xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); - xhr.setRequestHeader("Cache-Control", "max-age=0"); - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(theSession, xhr); - - _addWithCredentialsAndAccept(xhr, "application/json"); - - try { - - // Note: this gives a developer a way to change certain aspects of how we do the - // form-based login, but we will still be assuming that we are going directly to - // j_spring_security_check and including credentials in the body. They really should not - // try to change that. - // - if (typeof theSession.onOpenRequest === 'function') { - var cbparams = { - "xhr": xhr, - "verb": "POST", - "uri": theSession.serviceURI + "/static/auth/j_spring_security_check", - "async": xhr._isAsync, - "formPreTest": false, - "session": theSession - }; - theSession.onOpenRequest(cbparams); - xhr = cbparams.xhr; - } - - if (xhr._isAsync) { - xhr.onreadystatechange = theSession._onReadyStateChangeGeneric; - xhr.onResponseFn = theSession._afterFormLogin; - xhr.onResponseProcessedFn = theSession._loginComplete; - } - - // j_username=username&j_password=password&submit=Submit - xhr.send("j_username=" + encodeURIComponent(args.uname) + "&j_password=" + encodeURIComponent(args.pw) + "&submit=Submit"); - } - catch (e) { - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, theSession); - setLoginHttpStatus(xhr.status, theSession); - // null the temporary credentials variables - unameSave = null; - pwSave = null; - throw e; - } - - } - - if (xhr._isAsync && !needAuth) { - xhr.onResponseProcessedFn = theSession._loginComplete; - return theSession._afterFormLogin(xhr); - } - if (!xhr._isAsync) { - return theSession._afterFormLogin(xhr); - } - - } - - this._afterFormLogin = function (xhr) { - // check what we got - var theSession = xhr.pdsession; - var params = { - "session": theSession, - "xhr": xhr, - "statusFromjson": null - }; - var contentType = xhr.getResponseHeader("Content-Type"); - - if (contentType && contentType.indexOf("application/json") >= 0) { - handleJSONLoginResponse(params); - if (!params.statusFromjson) { - throw new Error( - "Internal OpenEdge Mobile client error handling login response. HTTP status: " + - xhr.status + "."); - } - else { - setLoginHttpStatus(params.statusFromjson, theSession); - } - } - else { - if (xhr.status === 200) { - // Was the response actually the login failure page or the login page itself (in case - // the appSecurity config file sets the login failure url so the server sends the login - // page again)? If so, call it an error because the credentials apparently failed to be - // authenticated - if (_gotLoginFailure(xhr) || _gotLoginForm(xhr)) { - setLoginHttpStatus(401, theSession); - } - else { - setLoginHttpStatus(xhr.status, theSession); - } - } - } - - return theSession._processLoginResult(xhr); - }; - - - this._processLoginResult = function (xhr) { - /* OK, one way or another, by hook or by crook, the Session object's loginHttpStatus - * has been set to the value that indicates the real outcome of the - * login, after adjusting for form-based authentication and anything - * else. At this point, it should be just a matter of examining - * this.loginHttpStatus, using it to set this.loginResult, maybe doing - * some other work appropriate to the outcome of the login, and returning - * this.loginResult. - */ - var pdsession = xhr.pdsession; - - setLoginHttpStatus(xhr.status, xhr.pdsession); - - if (pdsession.loginHttpStatus === 200) { - setLoginResult(progress.data.Session.LOGIN_SUCCESS, pdsession); - setRestApplicationIsOnline(true); - setUserName(unameSave, pdsession); - _password = pwSave; - pdsession._saveClientContextId(xhr); - storeAllSessionInfo(); // save info to persistent storage - - var pingTestArgs = { - pingURI: null, async: true, onCompleteFn: null, - fireEventIfOfflineChange: true, onReadyStateFn: pdsession._pingtestOnReadyStateChange - }; - pingTestArgs.pingURI = pdsession._makePingURI(); - pdsession._sendPing(pingTestArgs); // see whether the ping feature is available - } - else { - if (pdsession.loginHttpStatus == 401) { - setLoginResult(progress.data.Session.LOGIN_AUTHENTICATION_FAILURE, pdsession); - } - else { - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, pdsession); - } - } - setLastSessionXHR(xhr, pdsession); - updateContextPropsFromResponse(pdsession, xhr); - - // null the temporary credentials variables - unameSave = null; - pwSave = null; - if (xhr._iosTimeOutExpired) { - throw new Error( progress.data._getMsgText("jsdoMSG047", "login") ); - } - - // return loginResult even if it's an async operation -- the async handler - // (e.g., onReadyStateChangeGeneric) will just ignore - return pdsession.loginResult; - }; - - - this._loginComplete = function (pdsession, result, errObj, xhr) { - pdsession.trigger("afterLogin", pdsession, result, errObj, xhr); - }; - - // GET RID OF progress.data.Session logout CODE (AND RELATED) IF WE DROP SUPPORT FOR USING - // THE OLD progress.data.Session API DIRECTLY (mainly a problem for existing code (tdriver), - // or anyone who wants to call methods synchronously, which should be no one) - this.logout = function (args) { - var isAsync = false, - errorObject = null, - xhr, - deferred, - params; - - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // Session: Cannot call logout() when authenticationModel is SSO. - // Please use the AuthenticationProvider object instead. - throw new Error(progress.data._getMsgText("jsdoMSG057", 'Session', 'logout()')); - } - - if (this.loginResult !== progress.data.Session.LOGIN_SUCCESS && this.authenticationModel) { - throw new Error("Attempted to call logout when there is no active session."); - } - - if (typeof(args) === 'object') { - isAsync = args.async; - if (isAsync && (typeof isAsync !== 'boolean')) { - throw new Error( progress.data._getMsgText("jsdoMSG033", - "Session", - 'logout', - 'The async argument was invalid.')); - } - /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes - deferred and jsdosession and we need to eventually attach them to the XHR we use - so that the promise created by the JSDOSession will work correctly - */ - deferred = args.deferred; - } - - xhr = new XMLHttpRequest(); - xhr.pdsession = this; - try { - /* logout when auth model is anonymous is a no-op on the server side - (but we need to set _jsdosession and _deferred anyway to make promise work - if logout was called by a JSDOSession) */ - xhr._jsdosession = jsdosession; // in case the caller is a JSDOSession - xhr._deferred = deferred; // in case the caller is a JSDOSession - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_FORM || - this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { - if (isAsync) { - xhr.onreadystatechange = this._onReadyStateChangeGeneric; - xhr.onResponseFn = this._processLogoutResult; - xhr.onResponseProcessedFn = this._logoutComplete; - } - - - xhr.open('GET', this.serviceURI + "/static/auth/j_spring_security_logout", isAsync); - - /* instead of calling _addWithCredentialsAndAccept, we code the withCredentials - * and setRequestHeader inline so we can do it slightly differently. That - * function deliberately sets the request header inside the try so we don't - * run into a FireFox oddity that would give us a successful login and then - * a failure on getCatalog (see the comment on that function). On logout, - * however, we don't care -- just send the Accept header so we can get a 200 - * response - */ - try { - xhr.withCredentials = true; - } - catch (e) { - } - - xhr.setRequestHeader("Accept", "application/json"); - - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(this, xhr); - - if (typeof this.onOpenRequest === 'function') { - setLastSessionXHR(xhr, this); - params = { - "xhr": xhr, - "verb": "GET", - "uri": this.serviceURI + "/static/auth/j_spring_security_logout", - "async": false, - "formPreTest": false, - "session": this - }; - this.onOpenRequest(params); - xhr = params.xhr; - } - - setLastSessionXHR(xhr, this); - xhr.send(); - } - else { - xhr._anonymousLogoutOK = true; - } - } - catch (e) { - this._reinitializeAfterLogout(this, false); - throw e; - } - - if (!isAsync) { - try { - this._processLogoutResult(xhr); - } - catch (e) { - throw e; - } - } - - if (isAsync && this.authenticationModel === progress.data.Session.AUTH_TYPE_ANON) { - // fake async for Anonymous -- fire afterLogout event - try { - this._processLogoutResult(xhr); - } - catch (e) { - errorObject = e; - } - this._logoutComplete(this, null, errorObject, xhr); - } - - }; - - // This function erases all evidence of itself from the ServicesManager and - // flips a bit to prevent it to be used in the future - this.invalidate = function () { - isInvalidated = true; - cleanServicesManager(); - }; - - this._logoutComplete = function (pdsession, result, errorObject, xhr) { - // ignore result, it doesn't apply to logout -- is probably null or GENERAL_FAILURE - // we include it so onReadyStateChangeGeneric calls this correctly - pdsession.trigger("afterLogout", pdsession, errorObject, xhr); - }; - - this._processLogoutResult = function (xhr) { - var logoutSucceeded; - var pdsession = xhr.pdsession; - var basicStatusOK = false; - - if (xhr._anonymousLogoutOK) { - logoutSucceeded = true; - } - else if (xhr.status !== 200) { - /* Determine whether an error returned from the server is really an error - */ - if (pdsession.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { - /* If the Auth model is Basic, we probably got back a 404 Not found. - * But that's OK, because logout from Basic is meaningless on the - * server side unless it happens to be stateful, which is the only - * reason we even try calling j_spring_security_logout - */ - if (xhr.status === 404) { - logoutSucceeded = true; - } - else { - logoutSucceeded = false; - throw new Error("Error logging out, HTTP status = " + xhr.status); - } - } - else { - // for Form auth, any error on logout is an error - logoutSucceeded = false; - - // page refresh - we should call _reinitializeAfterLogout, or do something, so that - // caller can try logging in again (this is not a problem specific to page refresh, - // but the case of a page refresh after a server has gone down emphasizes it) - - throw new Error("Error logging out, HTTP status = " + xhr.status); - } - } - else { - logoutSucceeded = true; - } - - updateContextPropsFromResponse(pdsession, xhr); - pdsession._reinitializeAfterLogout(pdsession, logoutSucceeded); - }; - - this._reinitializeAfterLogout = function (pdsession, success) { - setLoginResult(null, pdsession); - setLoginHttpStatus(null, pdsession); - setClientContextID(null, pdsession); - setUserName(null, pdsession); - _password = null; - setAuthProvider(null); - - if (success) { - setRestApplicationIsOnline(false); - setOepingAvailable(false); - setPartialPingURI(defaultPartialPingURI); - setLastSessionXHR(null, pdsession); - clearTimeout(_timeoutID); // stop autopinging - } - }; - - - /* addCatalog - * - */ - this.addCatalog = function (arg1, arg2, arg3, arg4) { - var catalogURI, - catalogUserName, - catalogPassword, - isAsync = false, - xhr, - deferred, - iOSBasicAuthTimeout, - catalogIndex, - authProvider, - that = this; - - function addCatalogAfterOpen() { - /* This is here as much for CORS situations as the possibility that there might be an - * out of date cached version of the catalog. The CORS problem happens if you have - * accessed the catalog locally and then run an app on a different server that requests - * the catalog. Your browser already has the catalog, but the request used to get it was - * a non-CORS request and the browser will raise an error - */ - progress.data.Session._setNoCacheHeaders(xhr); - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(that, xhr); - - if (isAsync) { - xhr.onreadystatechange = that._onReadyStateChangeGeneric; - xhr.onResponseFn = that._processAddCatalogResult; - xhr.onResponseProcessedFn = that._addCatalogComplete; - - if (that.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC - && isUserAgentiOS - && iOSBasicAuthTimeout) { - xhr._requestTimeout = setTimeout(function () { - clearTimeout(xhr._requestTimeout); - xhr._iosTimeOutExpired = true; - xhr.abort(); - }, - iOSBasicAuthTimeout); - } - - // in case the caller is a JSDOSession - xhr._jsdosession = jsdosession; - xhr._deferred = deferred; - xhr._catalogIndex = catalogIndex; - } - - try { - if (typeof that.onOpenRequest === 'function') { - setLastSessionXHR(xhr, that); - var params = { - "xhr": xhr, - "verb": "GET", - "uri": catalogURI, - "async": false, - "formPreTest": false, - "session": that - }; - that.onOpenRequest(params); - xhr = params.xhr; - } - - setLastSessionXHR(xhr, that); - xhr.send(null); - } catch (e) { - throw new Error("Error retrieving catalog '" + catalogURI + "'.\n" + e.message); - } - if (isAsync) { - return progress.data.Session.ASYNC_PENDING; - } else { - return that._processAddCatalogResult(xhr); - } - - } - - if (this._isInvalidated) { - // JSDOSession: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); - } - - // Assume we're using a custom username/pw/authprovider - customCredentials = true; - - // check whether the args were passed in a single object. If so, copy them - // to the named arguments and a variable - if (arguments.length > 0) { - if (typeof arg1 === 'object') { - // check whether it's OK to add a catalog whilst offline - if (!arguments[0].offlineAddCatalog) { - if ((this.loginResult !== progress.data.Session.LOGIN_SUCCESS - && !this._authProvider) - && this.authenticationModel) { - throw new Error("Attempted to call addCatalog when there is no active session."); - } - } - - catalogURI = arg1.catalogURI; - if (!catalogURI || (typeof catalogURI !== 'string')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', - 'The catalogURI argument was missing or invalid.')); - } - catalogUserName = arg1.userName; - if (catalogUserName && (typeof catalogUserName !== 'string')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', - 'The catalogUserName argument was invalid.')); - } - catalogPassword = arg1.password; - if (catalogPassword && (typeof catalogPassword !== 'string')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', - 'The catalogPassword argument was invalid.')); - } - isAsync = arg1.async; - if (isAsync && (typeof isAsync !== 'boolean')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', - 'The async argument was invalid.')); - } - iOSBasicAuthTimeout = arg1.iOSBasicAuthTimeout; - if (typeof iOSBasicAuthTimeout === 'undefined') { - iOSBasicAuthTimeout = defaultiOSBasicAuthTimeout; - } else if (iOSBasicAuthTimeout && (typeof iOSBasicAuthTimeout !== 'number')) { - throw new Error(progress.data._getMsgText("jsdoMSG033", 'Session', 'addCatalog', - 'The iOSBasicAuthTimeout argument was invalid.')); - } - authProvider = arg1.authProvider; - - /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes - deferred, jsdosession, and catalogIndex and we need to eventually attach them to the - XHR we use so that the promise created by the JSDOSession will work correctly - */ - deferred = arg1.deferred; - catalogIndex = arg1.catalogIndex; - } else { - catalogURI = arg1; - if (typeof catalogURI !== 'string') { - throw new Error("First argument to Session.addCatalog must be the URL of the catalog."); - } - catalogUserName = arg2; - if (catalogUserName && (typeof catalogUserName !== 'string')) { - throw new Error("Second argument to Session.addCatalog must be a user name string."); - } - catalogPassword = arg3; - if (catalogPassword && (typeof catalogPassword !== 'string')) { - throw new Error("Third argument to Session.addCatalog must be a password string."); - } - } - } else { - throw new Error("Session.addCatalog is missing its first argument, the URL of the catalog."); - } - - if (!authProvider) { - authProvider = this._authProvider; - - // Guess we're using the default credentials passed earlier - customCredentials = false; - } - - // TODO: we expect that there will always be an authProvider if a login has been done. - // Therefore, we don't need to set catalogUsername and catalogPassword if they aren't - // passed in. What we should do here, when we extend the AuthenticationProvider API - // for the older auth models, is take any uname and pw passed in and create an auth - // provider, log in to the catalogURI with it, create an authImpl, and then fetch the - // catalog. - if (!catalogUserName) { - catalogUserName = this.userName; - } - - if (!catalogPassword) { - catalogPassword = _password; - } - - xhr = new XMLHttpRequest(); - xhr.pdsession = this; - xhr._catalogURI = catalogURI; - - // for now we don't support multiple version of the catalog across sessions - if (progress.data.ServicesManager.getSession(catalogURI) !== undefined) { - if (isAsync) { - /* - Attempt to get the event to fire AFTER this call returns ASYNC_PENDING - (and if the method was called from a JSDOSession, create an xhr to communicate - information related to promises back to its afterAddCatalog handler). Note that - the xhr is never used to make a request, it just carries data in the way - expected by the handler) - */ - // in case the caller is a JSDOSession - xhr._jsdosession = jsdosession; - xhr._deferred = deferred; - xhr._catalogIndex = catalogIndex; - - setTimeout(this._addCatalogComplete, 10, this, - progress.data.Session.CATALOG_ALREADY_LOADED, null, xhr); - return progress.data.Session.ASYNC_PENDING; - } - return progress.data.Session.CATALOG_ALREADY_LOADED; - } - - if (authProvider) { - authProvider._openRequestAndAuthorize(xhr, 'GET', catalogURI, isAsync, addCatalogAfterOpen); - // existing code in JSDOSession addCatalog expects to get this as a return value, - // have to return it now - return progress.data.Session.ASYNC_PENDING; - } else { // should be able to get rid of this if we do away with synchronous (old Session API) support - this._setXHRCredentials(xhr, 'GET', catalogURI, catalogUserName, catalogPassword, isAsync); - // Note that we are not adding the CCID to the URL or as a header, because the catalog may not - // be stored with the REST app and even if it is, the AppServer ID shouldn't be relevant - - return addCatalogAfterOpen(); - } - - }; - - this._processAddCatalogResult = function (xhr) { - var _catalogHttpStatus = xhr.status; - var theSession = xhr.pdsession; - var servicedata; - var catalogURI = xhr._catalogURI, - serviceURL, - theJSDOSession = jsdosession; - - // Only change the Session's state if the default AuthProv is being used - if (!customCredentials) { - toggleOnlineState(xhr); - } - - if ((_catalogHttpStatus == 200) || (_catalogHttpStatus === 0) && xhr.responseText) { - servicedata = theSession._parseCatalog(xhr); - try { - progress.data.ServicesManager.addCatalog(servicedata, theSession); - } - catch (e) { - if (progress.data.ServicesManager.getSession(catalogURI) !== undefined) { - /* this failed because the catalog had already been loaded, but the code - in addCatalog did not catch that, probably because we are executing - the JSDOSession addCatalog with multiple catalogURIs passed, and 2 - are the same - */ - return progress.data.Session.CATALOG_ALREADY_LOADED; - } - // different catalogs, with same resource name - throw new Error("Error processing catalog '" + catalogURI + "'. \n" + e.message); - } - // create a mobile service object and add it to the Session's array of same - for (var i = 0; i < servicedata.length; i++) { - serviceURL = theSession._prependAppURL(servicedata[i].address); - pushService(new progress.data.MobileServiceObject( - { - name: servicedata[i].name, - uri: serviceURL - }), - theSession); - - if (servicedata[i].settings - && servicedata[i].settings.useXClientProps - && !theSession.xClientProps) { - console.warn("Catalog warning: Service settings property 'useXClientProps' " + - "is true but 'xClientProps' property has not been set."); - } - } - pushCatalogURIs(catalogURI, theSession); - progress.data.ServicesManager.addSession(catalogURI, theSession); - if (theJSDOSession) { - progress.data.ServicesManager.addJSDOSession(catalogURI, theJSDOSession); - } - } - else if (_catalogHttpStatus == 401) { - return progress.data.AuthenticationProvider._getAuthFailureReason(xhr); - } - else if (xhr._iosTimeOutExpired) { - throw new Error( progress.data._getMsgText("jsdoMSG047", "addCatalog") ); - } - else { - throw new Error("Error retrieving catalog '" + catalogURI + - "'. Http status: " + _catalogHttpStatus + "."); - } - - return progress.data.Session.SUCCESS; - }; - - this._addCatalogComplete = function (pdsession, result, errObj, xhr) { - pdsession.trigger("afterAddCatalog", pdsession, result, errObj, xhr); - }; - - - /* - * ping -- determine whether the Mobile Web Application that the Session object represents - * is available, which includes determining whether its associated AppServer is running - * Also determine whether the Mobile services managed by this Session object are available - * (which means simply that they're known to the Mobile Web Application) - * (Implementation note: be sure that this Session object's "connected" - * property retains its current value until the end of this function, where - * it gets updated, if necessary, after calling _isOnlineStateChange - * - * Signatures : - * @param arg - * There are 2 signatures -- - * - no argument -- do an async ping of the Session's Mobile Web application. The only effect - * of the ping will be firing an offline or an online event, if appropriate - * The ping function itself will return false to the caller - * - object argument -- the object's properties provide the input args. They are all - * optional (if for some reason the caller passes an object that has no properties, it's - * the same as passing no argument at all). The properties may be: - * async -- tells whether to execute the ping asynchronously (which is the default) - * onCompleteFn -- if async, this will be called when response returns - * doNotFireEvent -- used internally, controls whether the ping method causes an offline - * or online event to be fired if there has been a change (the default is that it - * does, but our Session._checkServiceResponse() sets this to true so that it can - * control the firing of the event) - * offlineReason -- if present, and if the ping code discovers that teh server is offline, - * the ping code will set this with its best guess - * as to the reason the server is offline - */ - this.ping = function (args) { - var pingResult = false; - var pingArgs = { - pingURI: null, async: true, onCompleteFn: null, - fireEventIfOfflineChange: true, onReadyStateFn: this._onReadyStateChangePing, - offlineReason: null - }; - - if (this._isInvalidated) { - // Session: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "Session")); - } - - if ((!this._authProvider) && (this.loginResult !== progress.data.Session.LOGIN_SUCCESS)) { - throw new Error("Attempted to call ping when not logged in."); - } - - if (args) { - if (args.async !== undefined) { - // when we do background pinging (because pingInterval is set), - // we pass in an arg that is just an object that has an async property, - // set to true. This can be expanded to enable other kinds of ping calls - // to be done async (so that application developers can do so, if we decide - // to support that) - pingArgs.async = args.async; - } - - if (args.doNotFireEvent !== undefined) { - pingArgs.fireEventIfOfflineChange = !args.doNotFireEvent; - } - - if (args.onCompleteFn && (typeof args.onCompleteFn) == 'function') { - pingArgs.onCompleteFn = args.onCompleteFn; - } - /* Special for JSDOSession: if this method was called by a JSDOSession object, it passes - deferred and jsdosession and we need to eventually attach them to the XHR we use so that - the promise created by the JSDOSession will work correctly - */ - pingArgs.deferred = args.deferred; - pingArgs.jsdosession = args.jsdosession; - - } - - - /* Ping the Mobile Web Application (this will also determine whether AppServer is available) - * Call _processPingResult() if we're synchronous, otherwise the handler for the xhr.send() - * will call it - */ - pingArgs.pingURI = that._makePingURI(); - that._sendPing(pingArgs); - if (!pingArgs.async) { - if (pingArgs.xhr) { - pingResult = that._processPingResult(pingArgs); - if (args.offlineReason !== undefined) { - args.offlineReason = pingArgs.offlineReason; - } - } - else { - pingResult = false; // no xhr returned from _sendPing, something must have gone wrong - } - if ( args.xhr !== undefined ) { - // if it's a sync ping, return the xhr if caller indicates they want it - // (there's almost guaranteed to be one, even if the ping was never sent - // if for some reason there isn't, we give them the null or undefined we ended up with) - args.xhr = pingArgs.xhr; - } - } - // else it's async, deliberately returning false - // so developer not misled into thinking the ping succeeded - - return pingResult; - }; - - - // "protected" Functions - - /* - * given a value of true or false for being online for the Mobile Web Application - * managed by this Session object, determine whether that changes the current - * state of being offline or online. - * Returns true if the input state is a change from the current state - * - * Signature : - * @param isOnline Required. True to determine whether online is a state change, false to - * determine whether offline constitutes a state change. Boolean. - * - */ - this._isOnlineStateChange = function (isOnline) { - var stateChanged = false; - - if (isOnline && !(this.connected)) { - stateChanged = true; - } - else if (!isOnline && ( this.connected )) { - stateChanged = true; - } - - return stateChanged; - }; - - - /* - * given information about the response from a request made to a service, - * do the following: - * - * determine whether the online status of the Session has changed, and - * set the Session's Connected property accordingly - * if the Session's online status has changed, fire the appropriate event - * - * Signature : - * @param xhr Required. The xhr that was used to make the request. Object - * @param success Required. True if caller regards the request as having succeeded. Boolean - * @param request Required. The JSDO request object created for making the request. Object. - * - */ - this._checkServiceResponse = function (xhr, success, request) { - var offlineReason = null, - wasOnline = this.connected; - updateContextPropsFromResponse(this, xhr); - - /* first of all, if there are no subscriptions to offline or online events, don't - * bother -- we don't want to run the risk of messing things up by calling ping - * if the app developer isn't interested (especially because that may mean that - * ping isn't enabled on the server, anyway) - */ - if (!this._events) { - return; - } - var offlineObservers = this._events["offline"] || []; - var onlineObservers = this._events["online"] || []; - if ((offlineObservers.length === 0) && (onlineObservers.length === 0)) { - return; - } - - /* even though this function gets called as a result of trying to - * contact the server, don't bother to change anything if we already - * know that the device (or user agent, or client machine) is offline. - * We can't assume anything about the state of the server if we can't - * even get to the internet from the client - */ - - // if the call to the server was a success, we will assume we are online, - // both server and device - if (success) { - setRestApplicationIsOnline(true); - setDeviceIsOnline(true); // presumably this is true (probably was already true) - } - else { - /* Request failed, determine whether it's because server is offline - * Do this even if the Session was already in an offline state, because - * we need to determine whether the failure was due to still being - * offline, or whether it's now possible to communicate with the - * server but the problem was something else. - */ - - if (deviceIsOnline) { - /* ping the server to get better information on whether this is an offline case - * NB: synchronous ping for simplicity, maybe should consider async so as not - * to potentially freeze UI - */ - var localPingArgs = { - doNotFireEvent: true, // do in this fn so we have the request - offlineReason: null, - async: false - }; - if (!(that.ping(localPingArgs) )) { - offlineReason = localPingArgs.offlineReason; - setRestApplicationIsOnline(false); - } - else { - // ping returned true, so even though the original request failed, - // we are online and the failure must have been due to something else - setRestApplicationIsOnline(true); - } - } - // else deviceIsOnline was already false, so the offline event should already have - // been fired for that reason and there is no need to do anything else - } - - if (wasOnline && !this.connected) { - this.trigger("offline", this, offlineReason, request); - } - else if (!wasOnline && this.connected) { - this.trigger("online", this, request); - } - }; - - /* Decide whether, on the basis of information returned by a server request, the - * Mobile Web Application managed by this Session object is online, where online - * means that the ping response was a 200 and, IF the body of the response contains - * JSON with an AppServerStatus property, that AppServerStatus Status property has - * a pingStatus property set to true - * i.e., the body has an AppServerStatus.PingStatus set to true - * (if the body doesn't contain JSON with an AppServerStatus, we use just the HTTP - * response status code to decide) - * - * Returns: true if the response meets the above conditions, false if it doesn't - * - * Parameters: - * args, with properties: - * xhr - the XMLHttpRequest used to make the request - * offlineReason - if the function determines that the app is offline, - * it sets offlineReason to the reason for that decision, - * for the use of the caller - * fireEventIfOfflineChange - if true, the function fires an offline or online - * event if there has been a change (i.e., the online state determined - * by the function is different from what it had been when the function - * began executing) - * usingOepingFormat - OPTIONAL. The function's default assumption is that the value - * of the session's internal oepingAvailable variable indicates whether the - * the response body will be in the format used by the OpenEdge oeping service. - * A caller can override this assumption by using this property to true or false. - * (the isAuthorized code sets this to false because it doesn't use oeping - * but does call this function) - */ - this._processPingResult = function (args) { - var xhr = args.xhr, - pingResponseJSON, - appServerStatus = null, - wasOnline = this.connected, - connectedBeforeCallback, - assumeOepingFormat; - - if (args.hasOwnProperty('usingOepingFormat')) { - assumeOepingFormat = args.usingOepingFormat; - } else { - assumeOepingFormat = oepingAvailable; - } - - /* first determine whether the Web server and the Mobile Web Application (MWA) - * are available - */ - if (xhr.status >= 200 && xhr.status < 300) { - updateContextPropsFromResponse(this, xhr); - if (assumeOepingFormat) { - try { - pingResponseJSON = JSON.parse(xhr.responseText); - appServerStatus = pingResponseJSON.AppServerStatus; - } - catch (e) { - /* We got a successful response from calling our ping URI, but it - * didn't return valid JSON. If we think that the oeping REST API - * is available on the server (so we should have gotten valid - * json), log this to the console. - * - */ - console.error("Unable to parse ping response."); - } - } - toggleOnlineState(xhr); - } - else { - if (deviceIsOnline) { - if (xhr.status === 0) { - args.offlineReason = progress.data.Session.SERVER_OFFLINE; - setRestApplicationIsOnline(false); - } - else if ((xhr.status === 404) || (xhr.status === 410)) { - /* if we get a 404, it means the Web server is up, but it - * can't find the resource we requested (either _oeping or - * the login target), therefore the Mobile Web application - * must be unavailable (410 is Gone) - */ - args.offlineReason = progress.data.Session.WEB_APPLICATION_OFFLINE; - setRestApplicationIsOnline(false); - } - else { - /* There's some error, but we can't say for sure that it's because - * the Web application is unavailable. May be an authentication problem, - * internal server error, or for some reason our ping request was - * invalid (unlikely to happen if it previously succeeded). - * In particular, if the server uses Form authentication, it - * may have come back online but now the session id - * is no longer valid. - */ - setRestApplicationIsOnline(true); - } - } - else { - args.offlineReason = progress.data.Session.DEVICE_OFFLINE; - } - } - - // is the AppServer online? appServerStatus will be non-null only - // if the ping request returned 200, meaning the other things are OK - // (connection to server, Tomcat, Mobile Web application) - if (appServerStatus) { - if (appServerStatus.PingStatus === "false") { - args.offlineReason = progress.data.Session.APPSERVER_OFFLINE; - setRestApplicationIsOnline(false); - } - else { - setRestApplicationIsOnline(true); - } - } - - /* We call any async ping callback handler and then, after that returns, fire an - offline or online event if necessary. - When deciding whether to fire an event, the responsibility of this _processPingResult() - function is to decide about the event on the basis of the data returned from the ping - that it is currently processing. Therefore, since the ping callback that is just about - to be called could change the outcome of the event decision (for example, if the handler - calls logout(), thus setting Session.connected to false)), we save the current value of - Session.connected and use that saved value to decide about the event after the ping - handler returns. - (If the application programmer wants to get an event fired as a result of something - that happens in the ping handler, they should call a ping() *after* that. - */ - connectedBeforeCallback = this.connected; - - if ((typeof xhr.onCompleteFn) == 'function') { - xhr.onCompleteFn({ - pingResult: this.connected, - xhr: xhr, - offlineReason: args.offlineReason - }); - } - - // decide whether to fire an event, and if so do it - if (args.fireEventIfOfflineChange) { - if (wasOnline && !connectedBeforeCallback) { - that.trigger("offline", that, args.offlineReason, null); - } - else if (!wasOnline && connectedBeforeCallback) { - that.trigger("online", that, null); - } - } - - return this.connected; - }; - - - this._onReadyStateChangePing = function () { - var xhr = this; - var args; - - if (xhr.readyState == 4) { - args = { - xhr: xhr, - fireEventIfOfflineChange: true, - offlineReason: null - }; - that._processPingResult(args); - if (_pingInterval > 0) { - _timeoutID = setTimeout(that._autoping, _pingInterval); - } - } - }; - - this._pingtestOnReadyStateChange = function () { - var xhr = this; - - if (xhr.readyState == 4) { - var foundOeping = false; - if (xhr.status >= 200 && xhr.status < 300) { - foundOeping = true; - } - else { - setPartialPingURI(that.loginTarget); - console.warn("Default ping target not available, will use loginTarget instead."); - } - setOepingAvailable(foundOeping); - - // If we're here, we've just logged in. If pingInterval has been set, we need - // to start autopinging - if (_pingInterval > 0) { - _timeoutID = setTimeout(that._autoping, _pingInterval); - } - } - }; - - /* - * args: pingURI - * async - * onCompleteFn used only if async is true - * - * (deliberately not catching thrown error) - */ - this._sendPing = function (args) { - var xhr = new XMLHttpRequest(), - that = this; - - function sendPingAfterOpen() { - if (args.async) { - xhr.onreadystatechange = args.onReadyStateFn; - xhr.onCompleteFn = args.onCompleteFn; - xhr._jsdosession = jsdosession; // in case the Session is part of a JSDOSession - xhr._deferred = args.deferred; // in case the Session is part of a JSDOSession - } - progress.data.Session._setNoCacheHeaders(xhr); - // set X-CLIENT-PROPS header - setRequestHeaderFromContextProps(that, xhr); - if (that.authenticationModel === progress.data.Session.AUTH_TYPE_FORM) { - _addWithCredentialsAndAccept(xhr, - "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - } - xhr.send(null); - } - - try { - if (this._authProvider) { - this._authProvider._openRequestAndAuthorize(xhr, - 'GET', - args.pingURI, - args.async, - sendPingAfterOpen); - } else { - // get rid of this if we do away with synchronous support (i.e., customer use of - // old Session API) - this._setXHRCredentials(xhr, "GET", args.pingURI, this.userName, _password, args.async); - - // Sending the XHR request after opening the channel - if (xhr.readyState === 1) { - sendPingAfterOpen(); - } - } - } catch (e) { - args.error = e; - } - - args.xhr = xhr; - }; - - this._makePingURI = function () { - var pingURI = this.serviceURI + partialPingURI; - // had caching problem with Firefox in its offline mode - if (progress.data.Session._useTimeStamp) { - pingURI = progress.data.Session._addTimeStampToURL(pingURI); - } - return pingURI; - }; - - - /* - * autoping -- callback - */ - this._autoping = function () { - that.ping({async: true}); - }; - - - // TODO for API revamp: get rid of this method and replace it with implementations - // of AUthenticationImplementation.openRequest that are specific to the - // auth models (assuming we can use some sort of subclassing or interface design) - // (and when we remove this, remove the calls to it in this file) - /* _setXHRCredentials (intended for progress.data library use only) - * set credentials as needed, both via the xhr's open method and setting the - * Authorization header directly - */ - this._setXHRCredentials = function (xhr, verb, uri, userName, password, async) { - - // note that we do not set credentials if userName is null. - // Null userName indicates that the developer is depending on the browser to - // get and manage the credentials, and we need to make sure we don't interfere with that - if (userName - && this.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { - - // See the comment at the definition of the canPassCredentialsToOpen() function - // for why we pass credentials to open() in some cases but not others. (If we're not using - // Basic auth, we never pass credentials) - if (canPassCredentialsToOpen()) { - xhr.open(verb, uri, async, userName, password); - } - else { - xhr.open(verb, uri, async); - } - - // set Authorization header - var auth = _make_basic_auth(userName, password); - xhr.setRequestHeader('Authorization', auth); - } - else { - xhr.open(verb, uri, async); - } - }; - - /* _addCCIDtoURL (intended for progress.data library use only) - * Add the Client Context ID being used by a session on an OE REST application, if we have - * previously stored one from a response from the server - */ - this._addCCIDtoURL = function (url) { - var urlPart1, - urlPart2, - jsessionidStr, - index; - - if (this.clientContextId && (this.clientContextId !== "0")) { - // Should we test protocol, - // host and port in addition to path to ensure that jsessionid is only sent - // when request applies to the REST app (it might not be if the catalog is somewhere else) - if (url.substring(0, this.serviceURI.length) == this.serviceURI) { - jsessionidStr = ";" + "JSESSIONID=" + this.clientContextId; - index = url.indexOf('?'); - if (index == -1) { - url += jsessionidStr; // just append the jsessionid path parameter to the path - } - else { - // insert jsessionid path parameter before the first query parameter - urlPart1 = url.substring(0, index); - urlPart2 = url.substring(index); - url = urlPart1 + jsessionidStr + urlPart2; - } - } - } - return url; - }; - - /* _saveClientContextId (intended for progress.data library use only) - * If the CCID hasn't been set for the session yet, check the xhr for it and store it. - * (If it has been set, assume that the existing one is correct and do nothing. We could - * enhance this function by checking to see whether the new one matches the existing one. - * Not sure what to do if that's the case -- overwrite the old one? ignore the new one? - * Should at least log a warning or error - */ - this._saveClientContextId = function (xhr) { - // do this unconditionally (even if there is already a client-context-id), because - // if basic authentication is set up such that it uses sessions, and cookies are disabled, - // the server will generate a different session on each request and the X-CLIENT-CONTEXT-ID - // will therefore be different - setClientContextIDfromXHR(xhr, this); - }; - - this._parseCatalog = function (xhr) { - var jsonObject; - var catalogdata; - - try { - jsonObject = JSON.parse(xhr.responseText); - catalogdata = jsonObject.services; - } - catch (e) { - console.error("Unable to parse response. Make sure catalog has correct format."); - catalogdata = null; - } - - return catalogdata; - }; - - /* _prependAppURL - * Prepends the URL of the Web application - * (the 1st parameter passed to login, stored in this.serviceURI) - * to whatever string is passed in. If the string passed in is an absolute URL, this function does - * nothing except return a copy. This function ensures that the resulting URL has the correct number - * of slashes between the web app url and the string passed in (currently that means that if what's - * passed in has no initial slash, the function adds one) - */ - this._prependAppURL = function (oldURL) { - if (!oldURL) { - /* If oldURL is null, just return the app URL. (It's not the responsibility of this - * function to decide whether having a null URL is an error. Its only responsibility - * is to prepend the App URL to whatever it gets passed - * (and make sure the result is a valid URL) - */ - return this.serviceURI; - } - var newURL = oldURL; - var pat = /^https?:\/\//i; - if (!pat.test(newURL)) { - if (newURL.indexOf("/") !== 0) { - newURL = "/" + newURL; - } - - newURL = this.serviceURI + newURL; - } - return newURL; - }; - - - // Functions - - // get rid of this if we get rid of synchronous (old Session object API) support? - // Set an XMLHttpRequest object's withCredentials attribute and Accept header, - // using a try-catch so that if setting withCredentials throws an error it doesn't - // interrupt execution (this is a workaround for the fact that Firefox doesn't - // allow you to set withCredentials when you're doing a synchronous operation) - // The setting of the Accept header is included here, and happens after the - // attempt to set withCredentials, to make the behavior in 11.3.0 match - // the behavior in 11.2.1 -- for Firefox, in a CORS situation, login() will - // fail. (If we allowed the Accept header to be set, login() would succeed - // because of that but addCatalog() would fail because no JSESSIONID would - // be sent due to withCredentials not being true) - function _addWithCredentialsAndAccept(xhr, acceptString) { - try { - xhr.withCredentials = true; - xhr.setRequestHeader("Accept", acceptString); - } - catch (e) { - } - } - - // get rid of this if we get rid of synchronous (old Session API) support? - // (because it's in AuthenticationProviderBasic) - // from http://coderseye.com/2007/how-to-do-http-basic-auth-in-ajax.html - function _make_basic_auth(user, pw) { - var tok = user + ':' + pw; - var hash = btoa(tok); - return "Basic " + hash; - } - - /* The next 2 functions, _gotLoginForm() and _gotLoginFailure(), attempt to determine whether - * a server response consists of - * the application's login page or login failure page. Currently (release 11.2), this - * is the only way we have of determining that a request made to the server that's - * configured for form-based authentication failed due to authentication (i.e., - * authentication hadn't happened before the request and either invalid credentials or - * no credentials were sent to the server). That's because, due to the fact that the browser - * or native wrapper typically intercepts the redirect involved in an unauthenticated request - * to a server that's using using form auth, all we see in the XHR is a success status code - * plus whatever page we were redirected to. - * In the future, we expect to enhance the OE REST adapter so that it will return a status code - * indicating failure for form-based authentication, and we can reimplement these functions so - * they check for that code rather than do the simplistic string search. - */ - - // Determines whether the content of the xhr is the login page. Assumes - // use of a convention for testing for login page - var loginFormIDString = "j_spring_security_check"; - - function _gotLoginForm(xhr) { - // is the response contained in an xhr actually the login page? - return _findStringInResponseHTML(xhr, loginFormIDString); - } - - // Determines whether the content of the xhr is the login failure page. Assumes - // use of a convention for testing for login fail page - var loginFailureIdentificationString = "login failed"; - - function _gotLoginFailure(xhr) { - return _findStringInResponseHTML(xhr, loginFailureIdentificationString); - } - - // Does a given xhr contain html and does that html contain a given string? - function _findStringInResponseHTML(xhr, searchString) { - if (!xhr.responseText) { - return false; - } - var contentType = xhr.getResponseHeader("Content-Type"); - - if ((contentType.indexOf("text/html") >= 0) && - (xhr.responseText.indexOf(searchString) >= 0)) { - return true; - } - - return false; - } - - // get rid of this if we get rid of synchronous (old Session API) support? - /* sets the statusFromjson property in the params object to indicate - * the status of a response from an OE Mobile Web application that has - * to do with authentication (the response to a login request, or a - * response to a request for a resource where there was an error having - * to do with authentication */ - function handleJSONLoginResponse(params) { - // Parse the json in the response to see whether it's the special OE REST service - // response. If it is, check the result (which should be consistent with the status from - // the xhr) - var jsonObject; - params.statusFromjson = null; - try { - jsonObject = JSON.parse(params.xhr.responseText); - - if (jsonObject.status_code !== undefined - && jsonObject.status_txt !== undefined) { - params.statusFromjson = jsonObject.status_code; - } - } - catch (e) { - // invalid json - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, params.session); - setLoginHttpStatus(xhr.status, params.session); - throw new Error("Unable to parse login response from server."); - } - - } - - function setRequestHeaderFromContextProps(session, xhr) { - if (session.xClientProps) { - xhr.setRequestHeader("X-CLIENT-PROPS", session.xClientProps); - } - else if (session._contextProperties.contextHeader !== undefined) { - xhr.setRequestHeader("X-CLIENT-PROPS", session._contextProperties.contextHeader); - } - } - - function toggleOnlineState(xhr) { - var pdsession = that; - - setLoginHttpStatus(xhr.status, pdsession); - - if (pdsession.loginHttpStatus >= 200 && pdsession.loginHttpStatus < 400) { - setLoginResult(progress.data.Session.LOGIN_SUCCESS, pdsession); - setRestApplicationIsOnline(true); - pdsession._saveClientContextId(xhr); - storeAllSessionInfo(); // save info to persistent storage - } else { - // Taking a page from _processPingResult where we set the rest application as offline if it's one of - // these error codes - if (pdsession.loginHttpStatus === 0 || pdsession.loginHttpStatus === 400 || pdsession.loginHttpStatus === 410) { - setRestApplicationIsOnline(false); - setLoginResult(progress.data.AuthenticationProvider._getAuthFailureReason(xhr), - pdsession); - } - // Otherwise if it's probably an internal error or auth problem. Either way, we know it's still online. - else { - - setRestApplicationIsOnline(true); - setLoginResult(progress.data.Session.LOGIN_GENERAL_FAILURE, pdsession); - } - - - } - - setLastSessionXHR(xhr, pdsession); - updateContextPropsFromResponse(pdsession, xhr); - - return pdsession.loginResult; - }; - - function updateContextPropsFromResponse(session, xhr) { - /* determine whether the response contains an X-CLIENT_PROPS header and, if so, - set the Session's context - */ - var contextString, - context; - - if (xhr) { - contextString = getResponseHeaderNoError(xhr, "X-CLIENT-PROPS"); - if (contextString) { - try { - context = JSON.parse( contextString ); - } - catch(e) { - } - if (typeof context === "object") { - session._contextProperties.setContext( context ); - } - else { - //{1}: A server response included an invalid {2} header. - throw new Error(progress.data._getMsgText("jsdoMSG123", 'Session', 'X-CLIENT-PROPS')); - } - } - else if (contextString === "") { - // If header is "", clear the X-CLIENT-PROPS context, - session._contextProperties.setContext( {} ); - } - // if header is absent (getResponseHeader will return null), don't change _contextProperties - } - } - - // Remove all resources, services, and sessions related to this Session from the ServicesManager - function cleanServicesManager() { - progress.data.ServicesManager.cleanSession(that); - } - - // process constructor options and do other initialization - - // If a storage key (name property of a JSDOSession) was passed to the constructor, - // use it to try to retrieve state data from a previous JSDOSession instance that - // had the same name. This code was introduced to handle page refreshes, but could - // be used for other purposes. - if (typeof (options) === 'object') { - - jsdosession = options.jsdosession; - newURI = options.serviceURI; - setAuthProvider(options.authProvider); // do this BEFORE calling setSessionInfoFromStorage - - if (options.authProvider && options.authProvider.hasClientCredentials()) { - _loginResult = progress.data.Session.LOGIN_SUCCESS; - } - - // get rid of trailing '/' because appending service url that starts with '/' - // will cause request failures - if (newURI && newURI[newURI.length - 1] === "/") { - newURI = newURI.substring(0, newURI.length - 1); - } - - _storageKey = options._storageKey; - if (_storageKey) { - if (retrieveSessionInfo(_storageKey)) { - storedAuthModel = retrieveSessionInfo("authenticationModel"); - storedURI = retrieveSessionInfo("serviceURI"); - - if ((storedAuthModel !== options.authenticationModel) || - (storedURI !== newURI)) { - clearAllSessionInfo(); - } else { - // Note: be sure we have set authProvider (if any) from options before - // calling setSessionInfoFromStorage (important so that the logic in - // setSessionInfoFromStorage that re-creates an AuthenticationProvider - // after page refresh only gets used if the app is using the old JSDOSession.login) - setSessionInfoFromStorage(_storageKey); - stateWasReadFromStorage = true; - } - } - // _storageKey is in essence the flag for page refresh; we are not supporting page refresh for Basic - // auth, so clear it even if it was passed in. - // (But had to set and keep _storageKey until this point so that the above validation of - // serviceURI and auth model will be done even in the case where there's a mismatch and - // the new auth model is Basic. This statement will go away when we support page refresh with - // Basic) - if (options.authenticationModel === progress.data.Session.AUTH_TYPE_BASIC) { - _storageKey = undefined; - } - } - - // If we didn't read state info from storage, we need to set the serviceURI and probably - // the authenticationModel - if (!stateWasReadFromStorage) { - if (newURI) { - setServiceURI(newURI, this); - } - if (options.authenticationModel) { - this.authenticationModel = options.authenticationModel; - } - } - } - - }; // End of Session - progress.data.Session._useTimeStamp = true; - - var SEQ_MAX_VALUE = 999999999999999; - // 15 - 9 - var _tsseq = SEQ_MAX_VALUE; - // Initialized to SEQ_MAX_VALUE to initialize values. - var _tsprefix1 = 0; - var _tsprefix2 = 0; - - // this._getNextTimeStamp = function () { - progress.data.Session._getNextTimeStamp = function () { - var seq = ++_tsseq; - if (seq >= SEQ_MAX_VALUE) { - _tsseq = seq = 1; - var t = Math.floor(( Date.now ? Date.now() : (new Date().getTime())) / 10000); - if (_tsprefix1 == t) { - _tsprefix2++; - if (_tsprefix2 >= SEQ_MAX_VALUE) { - _tsprefix2 = 1; - } - } - else { - _tsprefix1 = t; - Math.random(); // Ignore call to random - _tsprefix2 = Math.round(Math.random() * 10000000000); - } - } - - return _tsprefix1 + "-" + _tsprefix2 + "-" + seq; - }; - - /* - * _addTimeStampToURL (intended for progress.data library use only) - * Add a time stamp to the a URL to prevent caching of the request. - * Set progress.data.Session._useTimeStamp = false to turn off. - */ - progress.data.Session._addTimeStampToURL = function (url) { - var timeStamp = "_ts=" + progress.data.Session._getNextTimeStamp(); - url += ((url.indexOf('?') == -1) ? "?" : "&") + timeStamp; - return url; - }; - - // Do whatever it takes to direct the XMLHttpRequest not to fulfill the request - // from a cache - // (convenience method --- we do this several different places in the code) - progress.data.Session._setNoCacheHeaders = function (xhr) { - xhr.setRequestHeader("Cache-Control", "no-cache"); - xhr.setRequestHeader("Pragma", "no-cache"); - }; - - - -// Constants for progress.data.Session - if ((typeof Object.defineProperty) == 'function') { - Object.defineProperty(progress.data.Session, 'LOGIN_AUTHENTICATION_REQUIRED', { - value: 0, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'LOGIN_SUCCESS', { - value: 1, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'LOGIN_AUTHENTICATION_FAILURE', { - value: 2, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'LOGIN_GENERAL_FAILURE', { - value: 3, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'CATALOG_ALREADY_LOADED', { - value: 4, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'ASYNC_PENDING', { - value: 5, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'EXPIRED_TOKEN', { - value: 6, enumerable: true - }); - - Object.defineProperty(progress.data.Session, 'SUCCESS', { - value: 1, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'AUTHENTICATION_FAILURE', { - value: 2, enumerable: true - }); - Object.defineProperty(progress.data.Session, 'GENERAL_FAILURE', { - value: 3, enumerable: true - }); - - Object.defineProperty(progress.data.Session, 'AUTH_TYPE_ANON', { - value: "anonymous", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'AUTH_TYPE_BASIC', { - value: "basic", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'AUTH_TYPE_FORM', { - value: "form", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'AUTH_TYPE_SSO', { - value: "sso", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'AUTH_TYPE_FORM_SSO', { - value: "form_sso", enumerable: true - }); - - - Object.defineProperty(progress.data.Session, 'DEVICE_OFFLINE', { - value: "Device is offline", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'SERVER_OFFLINE', { - value: "Cannot contact server", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'WEB_APPLICATION_OFFLINE', { - value: "Mobile Web Application is not available", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'SERVICE_OFFLINE', { - value: "REST web Service is not available", enumerable: true - }); - Object.defineProperty(progress.data.Session, 'APPSERVER_OFFLINE', { - value: "AppServer is not available", enumerable: true - }); - } - else { - progress.data.Session.LOGIN_SUCCESS = 1; - progress.data.Session.LOGIN_AUTHENTICATION_FAILURE = 2; - progress.data.Session.LOGIN_GENERAL_FAILURE = 3; - progress.data.Session.CATALOG_ALREADY_LOADED = 4; - - progress.data.Session.SUCCESS = 1; - progress.data.Session.AUTHENTICATION_FAILURE = 2; - progress.data.Session.GENERAL_FAILURE = 3; - - progress.data.Session.AUTH_TYPE_ANON = "anonymous"; - progress.data.Session.AUTH_TYPE_BASIC = "basic"; - progress.data.Session.AUTH_TYPE_FORM = "form"; - progress.data.Session.AUTH_TYPE_SSO = "sso"; - - /* deliberately not including the "offline reasons" that are defined in the - * 1st part of the conditional. We believe that we can be used only in environments where - * ECMAScript 5 is supported, so let's put that assumption to the test - */ - } - -//setup inheritance for Session -- specifically for incorporating an Observable object - progress.data.Session.prototype = new progress.util.Observable(); - progress.data.Session.prototype.constructor = progress.data.Session; - function validateSessionSubscribe(args, evt, listenerData) { - listenerData.operation = undefined; - var found = false; - - // make sure this event is one that we support - for (var i = 0; i < this._eventNames.length; i++) { - if (evt === this._eventNames[i].toLowerCase()) { - found = true; - break; - } - } - if (!found) { - throw new Error(progress.data._getMsgText("jsdoMSG042", evt)); - } - - if (args.length < 2) { - throw new Error(progress.data._getMsgText("jsdoMSG038", 2)); - } - - if (typeof args[0] !== 'string') { - throw new Error(progress.data._getMsgText("jsdoMSG039")); - } - - if (typeof args[1] !== 'function') { - throw new Error(progress.data._getMsgText("jsdoMSG040")); - } - else { - listenerData.fn = args[1]; - } - - if (args.length > 2) { - if (typeof args[2] !== 'object') { - throw new Error(progress.data._getMsgText("jsdoMSG041", evt)); - } - else { - listenerData.scope = args[2]; - } - } - } - // events supported by Session - progress.data.Session.prototype._eventNames = - ["offline", "online", "afterLogin", "afterAddCatalog", "afterLogout", "afterDisconnect"]; - // callback to validate subscribe and unsubscribe - progress.data.Session.prototype.validateSubscribe = validateSessionSubscribe; - progress.data.Session.prototype.toString = function (radix) { - return "progress.data.Session"; - }; - - - /* - progress.data.JSDOSession - Like progress.data.Session, but the methods are async-only and return promises. - (first implementation uses progress.data.Session to do the work, but conceivably - that implementation could be changed to something different) - The JSDOSession object keeps the same underlying pdsession object for the lifetime - of the JSDOSession object -- i.e., even after logout and subsequent login, the pdsession - is re-used rather than re-created. - */ - progress.data.JSDOSession = function JSDOSession(options) { - var _pdsession, - _serviceURI, - that = this, - _name; - - // PROPERTIES - // Approach: Use the properties of the underlying progress.data.Session object whenever - // possible. - Object.defineProperty(this, 'authenticationModel', - { - get: function () { - return _pdsession ? _pdsession.authenticationModel : undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'authProvider', - { - get: function () { - return _pdsession ? _pdsession._authProvider : null; - }, - enumerable: true - }); - Object.defineProperty(this, 'catalogURIs', - { - get: function () { - return _pdsession ? _pdsession.catalogURIs: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'clientContextId', - { - get: function () { - return _pdsession ? _pdsession.clientContextId: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'connected', - { - get: function () { - return _pdsession ? _pdsession.connected: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'JSDOs', - { - get: function () { - return _pdsession ? _pdsession.JSDOs: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'loginResult', - { - get: function () { - return _pdsession ? _pdsession.loginResult: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'loginHttpStatus', - { - get: function () { - return _pdsession ? _pdsession.loginHttpStatus: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'onOpenRequest', - { - get: function () { - return _pdsession ? _pdsession.onOpenRequest: undefined; - }, - set: function (newval) { - if (_pdsession) { - _pdsession.onOpenRequest = newval; - } - }, - enumerable: true - }); - - Object.defineProperty(this, 'pingInterval', - { - get: function () { - return _pdsession ? _pdsession.pingInterval: undefined; - }, - set: function (newval) { - if (_pdsession) { - _pdsession.pingInterval = newval; - } - }, - enumerable: true - }); - - Object.defineProperty(this, 'services', - { - get: function () { - return _pdsession ? _pdsession.services: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'serviceURI', - { - get: function () { - if (_pdsession && _pdsession.serviceURI) { - return _pdsession.serviceURI; - } - else { - return _serviceURI; - } - }, - enumerable: true - }); - - Object.defineProperty(this, 'userName', - { - get: function () { - return _pdsession ? _pdsession.userName: undefined; - }, - enumerable: true - }); - - Object.defineProperty(this, 'name', - { - get: function () { - return _name; - }, - enumerable: true - }); - - Object.defineProperty( - this, - "_isInvalidated", - { - get: function () { - return _pdsession._isInvalidated; - }, - enumerable: false - } - ); - - // PRIVATE FUNCTIONS - - - // Wrapper to make it easier to change the promise implementation we use. - // Note that in the JSDO library's first implementation of promise support, - // the "promise" parameter for this function is actually a jQuery Deferred object - function settlePromise(promise, fulfill, result, info) { - if (fulfill) { - promise.resolve(that, result, info); - } else { - promise.reject(that, result, info); - } - } - - // use this for the events fired by progress.data.Session that can be handled with common code - function genericSessionEventHandler(pdsession, result, errorObject, xhr, deferred) { - var myDeferred; - - if (xhr) { - myDeferred = xhr._deferred; - } else { - myDeferred = deferred; - } - - settlePromise(myDeferred, - result === progress.data.Session.SUCCESS ? true : false, - result, - { errorObject: errorObject, - xhr: xhr }); - } - - function onAfterAddCatalog( pdsession, result, errorObject, xhr ) { - var deferred, - fulfill = false, - settleResult; - - if (result === progress.data.Session.EXPIRED_TOKEN) { - settleResult = progress.data.Session.EXPIRED_TOKEN; - } else { - settleResult = progress.data.Session.GENERAL_FAILURE; - } - - if (xhr && xhr._deferred) { - deferred = xhr._deferred; - - /* add the result for this addCatalog to the result array. */ - if ( result !== progress.data.Session.SUCCESS && - result !== progress.data.Session.CATALOG_ALREADY_LOADED ) { - - result = result || progress.data.Session.GENERAL_FAILURE; - - /* Set a property on the deferred to indicates that the "overall" result was - a failure. When we decide whether to reject or resolve the promise, we reject - if it's set to GENERAL_FAILURE, otherwise we resolve the promise - (really only need to set this once, but simpler code if we just set (or possibly - re-set) it whenever we find an error, plus if, at some point while we're still - processing, it's important to know whether we've already had an error, we can - check the property) - */ - deferred._overallCatalogResult = progress.data.Session.GENERAL_FAILURE; - } - - deferred._results[xhr._catalogIndex] = { catalogURI : xhr._catalogURI, - result : result, - errorObject : errorObject, - xhr : xhr}; - deferred._numCatalogsProcessed += 1; - if ( deferred._numCatalogsProcessed === deferred._numCatalogs ) { - deferred._processedPromise = true; - - if ( !deferred._overallCatalogResult ) { - fulfill = true; - settleResult = progress.data.Session.SUCCESS; - } - settlePromise(xhr._deferred, - fulfill, - settleResult, - xhr._deferred._results); - } - } - } - - function onAfterLogout(pdsession, errorObject, xhr) { - var result = progress.data.Session.GENERAL_FAILURE, - fulfill = false; - if (xhr && xhr._deferred) { - /* Note: loginResult gets cleared on successful logout, so testing it for false - to confirm that logout succeeded - */ - if (!errorObject && !pdsession.loginResult) { - result = progress.data.Session.SUCCESS; - fulfill = true; - } - settlePromise(xhr._deferred, - fulfill, - result, - { errorObject: errorObject, - xhr: xhr }); - } - } - - function onPingComplete(args) { - var xhr = args.xhr; - if (xhr && xhr._deferred) { - settlePromise(xhr._deferred, - args.pingResult, // this tells settlePromise whether to resolve or reject - args.pingResult, // this is the result value passed to the promise handler - { offlineReason: args.offlineReason, - xhr: xhr }); - } - } - - // METHODS - - // login() - // Creates an AuthenticationProvider and calls its login() method. Any errors thrown by the - // Auth Provider's constructor or login will bubble up to the caller, otherwise this method - // returns the promise from the A-P's login call. - this.login = function (username, password, options) { - var deferred = $.Deferred(), - iOSBasicAuthTimeout; - - function callIsAuthorized() { - that.isAuthorized() - .then(function (jsdosession, result, info) { - deferred.resolve(that, result, info); - }, function (jsdosession, result, info) { - deferred.reject(that, result, info); - }); - } - - if (this._isInvalidated) { - // JSDOSession: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); - } - - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // JSDOSession: Cannot call login() when authenticationModel is SSO. - // Please use the AuthenticationProvider object instead. - throw new Error(progress.data._getMsgText("jsdoMSG057", - 'JSDOSession', - 'login()')); - } - - if (typeof options === 'object') { - iOSBasicAuthTimeout = options.iOSBasicAuthTimeout; - } - - if (!_pdsession._authProvider) { - // is there a better way to do this? Need it because we didn't have the authprovider when - // running the constructor - _pdsession._authProvider = new progress.data.AuthenticationProvider({ - uri: this.serviceURI, - authenticationModel: this.authenticationModel - }); - } - - - _pdsession._authProvider.logout() - .then( function () { - return _pdsession._authProvider.login(username, password); - }) - .then(function () { - callIsAuthorized(); - }, function (provider, result, info) { - deferred.reject(that, result, info); - }); - - return deferred.promise(); - }; - - // This method terminates the JSDOSession's ability to send requests to its serviceURI. - // Remove the reference to the AuthenticationProvider that was passed to connect(). - // Will be a no-op if connect() has not yet been called successfully. - // This method reinitializes the Session object back to the state it was in just after being created. - // Retains the serviceURI, authenticationModel, and name values. - // Delete any of the object's data that had been persisted (for example, to sessionStorage to support - // page refresh). - // Data for any catalogs loaded by the JSDOSession will NOT be deleted. - // See additional commecnts at the Session._disconnect method. - this.disconnect = function () { - var deferred = $.Deferred(), - errorObject; - - try { - _pdsession.subscribe('afterDisconnect', genericSessionEventHandler, this); - - _pdsession._disconnect(deferred); - } catch (e) { - // JSDOSession: Unexpected error calling disconnect: {e.message} - errorObject = new Error(progress.data._getMsgText("jsdoMSG049", "JSDOSession", "disconnect", e.message)); - } - - if (errorObject) { - throw errorObject; - } else { - return deferred.promise(); - } - }; - - this.addCatalog = function (catalogURI, unameOrOpts, password, opts) { - var deferred = $.Deferred(), - catalogURIs, - numCatalogs, - catalogIndex, - addResult, - errorObject, - iOSBasicAuthTimeout, - username, - options, - authProvider; - - if (this._isInvalidated) { - // JSDOSession: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); - } - - // check whether 1st param is a string or an array - if (typeof catalogURI === "string") { - catalogURIs = [catalogURI]; - } else if (catalogURI instanceof Array) { - catalogURIs = catalogURI; - } else { - throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "addCatalog", - "The first argument must be a string or an array of strings specifying the URI of the catalog.")); - } - - // type check the 2nd param if it exists - if (unameOrOpts) { - if (typeof unameOrOpts === "string") { - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // Session: Cannot pass username and password to addCatalog when - // authenticationModel is SSO. Pass an AuthenticationProvider instead. - throw new Error(progress.data._getMsgText("jsdoMSG058", 'Session')); - } - username = unameOrOpts; - // explictly ignore any authProvider if using the (catURI, uname, pw, options) signature - if (opts) { - options = opts; - options.authProvider = undefined; - } - } else if (typeof unameOrOpts === "object") { - options = unameOrOpts; - } else { - // JSDOSession: Argument 2 must be of type object in addCatalog call. - throw new Error(progress.data._getMsgText("jsdoMSG121", "JSDOSession", "2", - "object", "addCatalog")); - } - } - - if (typeof options === 'object') { - // possible override for the workaround for the Cordova iOS async Basic auth bug - iOSBasicAuthTimeout = options.iOSBasicAuthTimeout; - if (options.authProvider) { - authProvider = options.authProvider; - } else if (this.authProvider) { - authProvider = this.authProvider; - } - } - - // Error out if no authProvider or username was given - if (!authProvider && !this.authProvider && !username) { - throw new Error(progress.data._getMsgText("jsdoMSG511")); - } - - /* When we're done processing all catalogs, we pass an array of results to resolve() or - reject(). We're attaching this array to the deferred object, in case the app makes - multiple addCatalog calls (if the array was attached to the JSDOSession, - the 2nd call might overwrite the first) - */ - - /* Add properties to the deferred object for this call to store the total - number of catalogs that are to be done, the number that ahve been processed, - and a reference to an array of results. - Loop through the array of catalogURIs, calling addCatalog for each one. If a call - throws an error or returns something other than ASYNC_PENDING, create a result object - for that catalog and add the result object to the resultArray. Otherwise, the result - object will be added by the afterAddCatalog handler. - If all of the Session.addCatalog calls throw an error or return something other - than ASYNC_PENDING, this function will reject the promise and return. Otherwise - the afterAddCatalog handler will resolve or reject the promise after all calls have - been processed. - Note that we try to make sure that each entry in the results array is in the same position - as its catalogURI in the input array. - */ - // if a catalogURI has no protocol, pdsession will assume it's relative to the serviceURI, - // if there has been a login - // NOTE: this means if the app is trying to load a local catalog, it MUST - // specify the file: protocol (and we need to make sure that works on all platforms) - - _pdsession.subscribe('afterAddCatalog', onAfterAddCatalog, this); - - numCatalogs = catalogURIs.length; - deferred._numCatalogs = numCatalogs; - deferred._numCatalogsProcessed = 0; - deferred._results = []; - deferred._results.length = numCatalogs; - - for ( catalogIndex = 0; catalogIndex < numCatalogs; catalogIndex += 1) { - errorObject = undefined; - addResult = undefined; - try { - addResult = _pdsession.addCatalog( - { catalogURI : catalogURIs[catalogIndex], - async : true, - userName : username, - password : password, - deferred : deferred, - catalogIndex : catalogIndex, - iOSBasicAuthTimeout : iOSBasicAuthTimeout, - authProvider : authProvider, - offlineAddCatalog : true } ); // OK to get catalog if offline - } - catch (e) { - errorObject = new Error("JSDOSession: Unable to send addCatalog request. " + e.message); - } - - if ( addResult !== progress.data.Session.ASYNC_PENDING ) { - /* Set a property on the deferred to indicate that the "overall" result was - a failure. When we decide whether to reject or resolve the promise, we reject - if it's set to GENERAL_FAILURE, otherwise we resolve the promise - (really only need to set this once, but simpler code if we just set (or possibly - re-set) it whenever we find an error, plus if, at some point while we're still - processing, it's important to know whether we've already had an error, we can - check the property) - */ - deferred._overallCatalogResult = progress.data.Session.GENERAL_FAILURE; - if ( errorObject ) { - addResult = progress.data.Session.GENERAL_FAILURE; - } - deferred._results[catalogIndex] = { catalogURI : catalogURIs[catalogIndex], - result : addResult, - errorObject : errorObject, - xhr : undefined }; - deferred._numCatalogsProcessed += 1; - } - } - - if ( (deferred._numCatalogsProcessed === numCatalogs) && !deferred._processedPromise ) { - /* The goal here is to handle the case where all the catalogs - have been processed but the afterAddCatalog handler may not be invoked at the - end (the obvious example is if there are no async requests actually made by - Session.addCatalog). In that case, we have to resolve/reject from here. Chances are - very good that if we're doing this here, there's been at least one error, but just - to be sure, we check the deferred._overallCatalogResult anyway - */ - if ( deferred._overallCatalogResult === progress.data.Session.GENERAL_FAILURE ) { - deferred.reject( this, progress.data.Session.GENERAL_FAILURE, deferred._results ); - } - else { - deferred.resolve( this, progress.data.Session.SUCCESS, deferred._results ); - } - } - - return deferred.promise(); - }; - - // Note that this will work for either of these cases: - // - app originally called JSDOSession.login (so we implicitly created the AuthenticationProvider) - // - app created an AuthenticationProvider and passed it to connect, but now for some reason has - // called logout (this is actually a nice shortcut for someone who has used getSession) - // (NB: we should not allow this for SSO, tho) - // - // Note that we also don't support login/logout on the JSDOSession for page refresh - this.logout = function(){ - var deferred = $.Deferred(), - authProv = this.authProvider; - - if (this.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // JSDOSession: Cannot call logout() when authenticationModel is SSO. - // Please use the AuthenticationProvider object instead. - throw new Error(progress.data._getMsgText("jsdoMSG057", - 'JSDOSession', - 'logout()')); - } - - this.disconnect() - .then(function () { - if (authProv) { - return authProv.logout(); - } - // if there's no AP, just resolve immediately - deferred.resolve(that, progress.data.Session.SUCCESS, {}); - }) - .then(function (jsdosession, result, info) { - deferred.resolve(that, result, info); - }, - // catches errors on either login or connect - function (provider, result, info) { - deferred.reject(that, result, info); - } - ); - - return deferred.promise(); - }; - - this.invalidate = function () { - _pdsession.invalidate(); - return this.logout(); - }; - - this.ping = function() { - var deferred = $.Deferred(); - - if (this._isInvalidated) { - // JSDOSession: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); - } - - try { - _pdsession.ping( {async: true, - deferred : deferred, - onCompleteFn : onPingComplete } ); - } - catch(e) { - throw new Error("JSDOSession: Unable to send ping request. " + e.message); - } - - return deferred.promise(); - }; - - // Determine whether the JSDOSession can currently access its web application. - // The use expected for this method is to determine whether a JSDOSession that has - // previously authenticated to its web application still has authorization. - // For example, if the JSDOSession is using Form authentication, is the server - // session still valid or did it expire? - this.isAuthorized = function () { - var deferred = $.Deferred(), - xhr = new XMLHttpRequest(), - result, - that = this; - - if (this._isInvalidated) { - // JSDOSession: This session has been invalidated and cannot be used. - throw new Error(progress.data._getMsgText("jsdoMSG510", "JSDOSession")); - } - - // If we logged in successfuly using login() or if we have an AuthProvider, make the call - if (this.loginResult === progress.data.Session.LOGIN_SUCCESS || this.authProvider) { - _pdsession._openRequest(xhr, "GET", _pdsession.loginTarget, true, - function () { - xhr.onreadystatechange = function () { - // do we need this xhr var? The one declared in isAuthorized seems to be in scope - var xhr = this, - cbresult, - fakePingArgs, - info; - - if (xhr.readyState === 4) { - info = {xhr: xhr, - offlineReason: undefined, - fireEventIfOfflineChange: true, - usingOepingFormat: false - }; - - // call _processPingResult because it has logic for - // detecting change in online/offline state - _pdsession._processPingResult(info); - - if (xhr.status >= 200 && xhr.status < 300) { - deferred.resolve(that, - progress.data.Session.SUCCESS, - info); - } else { - if (xhr.status === 401) { - cbresult = progress.data.AuthenticationProvider._getAuthFailureReason(xhr); - } else { - cbresult = progress.data.Session.GENERAL_FAILURE; - } - deferred.reject(that, cbresult, info); - } - } - }; - - try { - xhr.send(); - } catch (e) { - throw new Error("JSDOSession: Unable to validate authorization. " + e.message); - } - } - ); - } else { - // Never logged in (or logged in and logged out). Regardless of what the reason - // was that there wasn't a login, the bottom line is that authentication is required - result = progress.data.Session.LOGIN_AUTHENTICATION_REQUIRED; - deferred.reject(that, result, {xhr: xhr}); - } - - return deferred.promise(); - }; - - /* - set the properties that are passed between client and Web application in the - X-CLIENT-PROPS header. This sets the complete set of properties all at once; - it replaces any existing context - */ - this.setContext = function( context ) { - _pdsession._contextProperties.setContext( context ); - }; - - /* - * Set or remove an individual property in the set of the properties that are passed - * between client and Web application in the X-CLIENT-PROPS header. This operates only - * on the property identiofied by propertyName; all other existing properties remain - * as they are. - * If the propertyName is not part of the context, this call adds it - * If it is part of the context, this call updates it, unless - - * If propertyValue is undefined, this call removes the property - */ - this.setContextProperty = function( propertyName, propertyValue) { - _pdsession._contextProperties.setContextProperty( propertyName, propertyValue ); - }; - - /* - * get the set of properties that are passed between client and Web application in the - * X-CLIENT-PROPS header. Returns an object that has the properties - */ - this.getContext = function( ) { - return _pdsession._contextProperties.getContext(); - }; - - /* get the value of an individual property that is in the set of properties passed between - * client and Web application in the X-CLIENT-PROPS header - */ - this.getContextProperty = function( propertyName) { - return _pdsession._contextProperties.getContextProperty( propertyName ); - }; - - - this._onlineHandler = function( session, request ) { - that.trigger( "online", that, request ); - }; - - this._offlineHandler = function( session, offlineReason, request ) { - that.trigger( "offline", that, offlineReason, request ); - }; - - // PROCESS CONSTRUCTOR ARGUMENTS - // validate constructor input arguments - if ( (arguments.length > 0) && (typeof(arguments[0]) === 'object') ) { - - // (options is the name of the arguments[0] parameter) - if (options.serviceURI && (typeof(options.serviceURI) === "string" ) ) { - _serviceURI = options.serviceURI; - } - else { - throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", - "The options parameter must include a 'serviceURI' property that is a string.") ); - } - - if (options.authenticationModel) { - if (typeof(options.authenticationModel) !== "string" ) { - throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", - "The authenticationModel property of the options parameter must be a string.") ); - } - - options.authenticationModel = options.authenticationModel.toLowerCase(); - } else { - options.authenticationModel = progress.data.Session.AUTH_TYPE_ANON; - } - - // TODO: clean this up. Maybe make an immediate function - if (options.authProvider) { - if (typeof options.authProvider !== 'object') { - // JSDOSession: The 'options' parameter passed to the 'constructor' function - // has an invalid value for the 'authProvider' property. - throw new Error(progress.data._getMsgText( - "jsdoMSG502", - "JSDOSession", - "options", - "constructor", - "authProvider" - )); - } - - if ((options.authProvider.authenticationModel !== progress.data.Session.AUTH_TYPE_FORM_SSO - && options.authProvider.authenticationModel !== options.authenticationModel) || - (options.authProvider.authenticationModel === progress.data.Session.AUTH_TYPE_FORM_SSO - && options.authenticationModel !== progress.data.Session.AUTH_TYPE_SSO)) { - // JSDOSession: Error in constructor. The authenticationModels of the " + - // AuthenticationProvider ({2}) and the JSDOSession ({3}) were not compatible."; - throw new Error(progress.data._getMsgText("jsdoMSG059", "JSDOSession", - options.authProvider.authenticationModel, options.authenticationModel)); - } - // Check if the provider exposes the required API. - if (typeof options.authProvider.hasClientCredentials === 'function') { - if (!options.authProvider.hasClientCredentials()) { - // JSDOSession: The AuthenticationProvider is not managing valid credentials. - throw new Error(progress.data._getMsgText("jsdoMSG125", "JSDOSession")); - } - } else { - // JSDOSession: AuthenticationProvider objects must have a hasClientCredentials method. - throw new Error(progress.data._getMsgText("jsdoMSG505", - "JSDOSession", - "AuthenticationProvider", - "hasClientCredentials")); - } - } else if (options.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - // JSDOSession: If a JSDOSession object is using the SSO authentication model, - // the options object passed to its constructor must include an authProvider property. - throw new Error(progress.data._getMsgText("jsdoMSG508")); - } - - } - else { - throw new Error(progress.data._getMsgText("jsdoMSG033", "JSDOSession", "the constructor", - "The options argument was missing or invalid.") ); - } - - _name = options.name; - - _pdsession = new progress.data.Session({_storageKey: _name, - _silent: true, - authenticationModel: options.authenticationModel, - serviceURI: options.serviceURI, - jsdosession: this, - authProvider: options.authProvider}); - - try { - if (options.context) { - this.setContext(options.context); - } - _pdsession.subscribe( "online", this._onlineHandler, this); - _pdsession.subscribe( "offline", this._offlineHandler, this); - } catch (err) { - _pdsession = undefined; // so it will be garbage collected - throw err; - } - - }; // end of JSDOSession - -//set up inheritance for JSDOSession -- specifically for incorporating an Observable object - progress.data.JSDOSession.prototype = new progress.util.Observable(); - progress.data.JSDOSession.prototype.constructor = progress.data.JSDOSession; - function validateJSDOSessionSubscribe(args, evt, listenerData) { - listenerData.operation = undefined; - var found = false; - - // make sure this event is one that we support - for (var i = 0; i < this._eventNames.length; i++) { - if (evt === this._eventNames[i].toLowerCase()) { - found = true; - break; - } - } - if (!found) { - throw new Error(progress.data._getMsgText("jsdoMSG042", evt)); - } - - if (args.length < 2) { - throw new Error(progress.data._getMsgText("jsdoMSG038", 2)); - } - - if (typeof args[0] !== 'string') { - throw new Error(progress.data._getMsgText("jsdoMSG039")); - } - - if (typeof args[1] !== 'function') { - throw new Error(progress.data._getMsgText("jsdoMSG040")); - } - else { - listenerData.fn = args[1]; - } - - if (args.length > 2) { - if (typeof args[2] !== 'object') { - throw new Error(progress.data._getMsgText("jsdoMSG041", evt)); - } - else { - listenerData.scope = args[2]; - } - } - } - // events supported by JSDOSession - progress.data.JSDOSession.prototype._eventNames = - ["offline", "online"]; - // callback to validate subscribe and unsubscribe - progress.data.JSDOSession.prototype.validateSubscribe = validateJSDOSessionSubscribe; - progress.data.JSDOSession.prototype.toString = function (radix) { - return "progress.data.JSDOSession"; - }; - - progress.data.getSession = function (options) { - var deferred = $.Deferred(), - authProvider, - promise, - authProviderInitObject = {}; - - // This is the reject handler for session-related operations - // login, addCatalog, and logout - function sessionRejectHandler(originator, result, info) { - // undo the AuthenticationProvider's login if it succeeded - if (authProvider && authProvider.hasClientCredentials()) { - authProvider.logout() - .always(function () { - deferred.reject(result, info); - }); - } else { - deferred.reject(result, info); - } - } - - // This is the reject handler for the login callback - function callbackRejectHandler(reason) { - deferred.reject(progress.data.Session.GENERAL_FAILURE, {"reason": reason}); - } - - function loginHandler(provider) { - var jsdosession; - - try { - jsdosession = new progress.data.JSDOSession(options); - try { - jsdosession.isAuthorized() - .then(function() { - return jsdosession.addCatalog(options.catalogURI); - }, sessionRejectHandler) - .then(function (jsdosession, result, info) { - deferred.resolve(jsdosession, progress.data.Session.SUCCESS); - }, sessionRejectHandler); - } catch (e) { - sessionRejectHandler(jsdosession, - progress.data.Session.GENERAL_FAILURE, - {errorObject: e}); - } - } catch (e) { - sessionRejectHandler(jsdosession, - progress.data.Session.GENERAL_FAILURE, - {errorObject: e}); - } - } - - // This function calls login using credentials from the appropriate source - // Note that as currently implemented, this should NOT be called when - // ANONYMOUS auth is being used, because it unconditionally returns - // AUTHENTICATION_FAILURE if there are no credentials and no loginCallback - function callLogin(provider) { - var errorObject; - - // Use the login callback if we are passed one - // NOTE: Do we even use logincallback? Remove this??? - if (typeof options.loginCallback !== 'undefined') { - options.loginCallback() - .then(function (result) { - try { - provider.login(result.username, result.password) - .then(loginHandler, sessionRejectHandler); - } catch (e) { - sessionRejectHandler( - provider, - progress.data.Session.GENERAL_FAILURE, - { - errorObject: e - } - ); - } - }, callbackRejectHandler); - } else if (options.username && options.password) { - try { - provider.login(options.username, options.password) - .then(loginHandler, sessionRejectHandler); - } catch (e) { - sessionRejectHandler( - provider, - progress.data.Session.GENERAL_FAILURE, - { - errorObject: e - } - ); - } - } else { - // getSession(): The login method was not executed because no credentials were supplied. - errorObject = new Error(progress.data._getMsgText( - "jsdoMSG052", - "getSession()" - )); - sessionRejectHandler( - provider, - progress.data.Session.AUTHENTICATION_FAILURE, - { - // including an Error object to make clear why there is no xhr (normally there would - // be one for an authentication failure) - errorObject: errorObject - } - ); - } - } - - if (typeof options !== 'object') { - // getSession(): 'options' must be of type 'object' - throw new Error(progress.data._getMsgText( - "jsdoMSG503", - "getSession()", - "options", - "object" - )); - } - - if (typeof options.loginCallback !== 'undefined' && - typeof options.loginCallback !== 'function') { - // getSession(): 'options.loginCallback' must be of type 'function' - throw new Error(progress.data._getMsgText( - "jsdoMSG503", - "getSession()", - "options.loginCallback", - "function" - )); - } - - // Create the AuthenticationProvider and let it handle the argument parsing - try { - // If authenticationURI is not set, use serviceURI (except for SSO) - // Note: the test will of course catch any value that evaluates to false, not just undefined or - // null (which are the main concern), but that's probably OK - if (options.authenticationModel === progress.data.Session.AUTH_TYPE_SSO) { - if (!options.authenticationURI || !options.authProviderAuthenticationModel) { - // "progress.data.getSession: If the getSession method is passed AUTH_TYPE_SSO as - // the authenticationModel, it must also be passed an authenticationURI and an - // authProviderAuthenticationModel." - throw new Error(progress.data._getMsgText("jsdoMSG509")); - } - } - - if (options.authenticationURI) { - authProviderInitObject.uri = options.authenticationURI; - authProviderInitObject.authenticationModel = options.authProviderAuthenticationModel; - - // if auth uri has been passed, there must be an authProviderAuthenticationModel - if (typeof authProviderInitObject.authenticationModel !== "string") { - // JSDOSession: The 'object' parameter passed to the 'getSession' function - // has an invalid value for the 'authProviderAuthenticationModel' property. - throw new Error(progress.data._getMsgText( - "jsdoMSG502", - "progress.data.getSession", - "object", - "getSession", - "authProviderAuthenticationModel" - )); - } - } else { - authProviderInitObject.uri = options.serviceURI; - authProviderInitObject.authenticationModel = options.authenticationModel; - } - - authProvider = new progress.data.AuthenticationProvider(authProviderInitObject); - options.authProvider = authProvider; - - if (authProvider.hasClientCredentials()) { - loginHandler(authProvider); - } else { - // If model is anon, just log in. - if (authProvider.authenticationModel === progress.data.Session.AUTH_TYPE_ANON) { - authProvider.login() - .then(loginHandler, sessionRejectHandler); - } else { - // We need to log-in with credentials. - callLogin(authProvider); - } - } - } catch (error) { - // throw error; - sessionRejectHandler( - null, - progress.data.Session.GENERAL_FAILURE, - { - errorObject: error - } - ); - } - - return deferred.promise(); - }; - - progress.data.invalidateAllSessions = function () { - var jsdosession, - key, - deferred = $.Deferred(), - jsdosessions = progress.data.ServicesManager._jsdosessions, - invalidatePromises = []; - - for (key in jsdosessions) { - if (jsdosessions.hasOwnProperty(key)) { - jsdosession = jsdosessions[key]; - - invalidatePromises.push(jsdosession.invalidate()); - } - } - - $.when.apply($, invalidatePromises) - .then(function () { - deferred.resolve(progress.data.Session.SUCCESS); - }, function (session, result, info) { - deferred.reject(progress.data.Session.GENERAL_FAILURE, info); - }); - - // Using beautiful jquery shenanigans - return deferred.promise(); - }; - -})(); - -if (typeof exports !== "undefined") { - exports.progress = progress; -} - -//# sourceURL=progress.jsdo.js -/* -progress.auth.js Version: 4.4.0-3 - -Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - "use strict"; // note that this makes JSLint complain if you use arguments[x] - - /*global progress : true*/ - /*global $ : false, storage, XMLHttpRequest*/ - - /* define these if not defined yet - they may already be defined if - progress.js was included first */ - if (typeof progress === "undefined") { - progress = {}; - } - if (typeof progress.data === "undefined") { - progress.data = {}; - } - - // This is really more along the lines of a Factory method in that it explicitly creates an object - // and returns it based on the the authModel parameter (rather than following the default JS - // pattern of adding properties to the "this" object created for it and passed in by the runtime). - // NOTE: If we support multiple AuthenticationProviders that get different tokens from the same - // server, we may need to add a "name" property to the initObject to use as a storage key - - progress.data.AuthenticationProvider = function (initObject) { - var authProv, - authModel, - uri; - - // process constructor arguments - if (typeof initObject === 'object') { - - // these 2 calls throw an appropriate error if the check doesn't pass - this._checkStringArg( - "constructor", - initObject.authenticationModel, - "initObject.authenticationModel", - "initObject.authenticationModel" - ); - - this._checkStringArg( - "constructor", - initObject.uri, - "init-object.uri", - "init-object.uri" - ); - } else { - // AuthenticationProvider: Invalid signature for constructor. The init-object argument - // was missing or invalid. - throw new Error(progress.data._getMsgText( - "jsdoMSG033", - "AuthenticationProvider", - "the constructor", - "The init-object argument was missing or invalid." - )); - } - - authModel = initObject.authenticationModel.toLowerCase(); - switch (authModel) { - case progress.data.Session.AUTH_TYPE_ANON: - this._initialize(initObject.uri, progress.data.Session.AUTH_TYPE_ANON, - {"_loginURI": progress.data.AuthenticationProvider._homeLoginURIBase}); - authProv = this; - break; - case progress.data.Session.AUTH_TYPE_BASIC: - authProv = new progress.data.AuthenticationProviderBasic(initObject.uri); - break; - case progress.data.Session.AUTH_TYPE_FORM: - authProv = new progress.data.AuthenticationProviderForm(initObject.uri); - break; - case progress.data.Session.AUTH_TYPE_FORM_SSO: - authProv = new progress.data.AuthenticationProviderSSO(initObject.uri); - break; - default: - // AuthenticationProvider: The 'init-object' parameter passed to the 'constructor' function - // has an invalid value for the 'authenticationModel' property. - throw new Error(progress.data._getMsgText( - "jsdoMSG502", - "AuthenticationProvider", - "init-object", - "constructor", - "authenticationModel" - )); - //break; - } - - return authProv; - }; - - - // ADD METHODS TO THE AuthenticationProvider PROTOYPE - - // GENERIC IMPLEMENTATION FOR login METHOD THAT THE API IMPLEMENTATIONS OF login CAN CALL - // (technically, they don't override it, they each have small login methods that call this) - progress.data.AuthenticationProvider.prototype._loginProto = - function (sendParam) { - var deferred = $.Deferred(), - xhr, - uriForRequest, - header, - that = this; - - if (this._loggedIn) { - // "The login method was not executed because the AuthenticationProvider is - // already logged in." - throw new Error(progress.data._getMsgText("jsdoMSG051", "AuthenticationProvider")); - } - - xhr = new XMLHttpRequest(); - - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - // process the response from the Web application - that._processLoginResult(xhr, deferred); - } - }; - - if (progress.data.Session._useTimeStamp) { - uriForRequest = progress.data.Session._addTimeStampToURL(this._loginURI); - } else { - uriForRequest = this._loginURI; - } - - this._openLoginRequest(xhr, uriForRequest); - - // We specify application/json for the response so that, if a bad request is sent, an - // OE Web application will directly send back a 401 with error info in the body as JSON. - // So we force the accept header to application/json because if we make an anonymous - // request to a FORM/BASIC backend, it might redirect us to a login page since we have - // no credentials. And since we can technically access JUST the login page, the XHR - // will identify it as SUCCESS. If we specify "application/json", no redirects will - // happen, just a plain old "401 GET OUTTA HERE" code. - xhr.setRequestHeader("Accept", "application/json"); - - xhr.send(sendParam); - return deferred.promise(); - }; - - - - // PUBLIC METHODS (and their "helpers") (documented as part of the JSDO library API) - - // login API method -- just a shell that calls loginProto - progress.data.AuthenticationProvider.prototype.login = function () { - return this._loginProto(); - }; - - // HELPER FOR login METHOD, PROBABLY OVERRIDDEN IN MOST CONSTRUCTORS - progress.data.AuthenticationProvider.prototype._openLoginRequest = function (xhr, uri) { - xhr.open('GET', uri, true); - progress.data.Session._setNoCacheHeaders(xhr); - }; - - // HELPER FOR login METHOD, PROBABLY OVERRIDDEN IN MOST CONSTRUCTORS - progress.data.AuthenticationProvider.prototype._processLoginResult = function (xhr, deferred) { - var result; - - if (xhr.status === 200) { - // Need to set loggedIn now so we can call logout from here if there's an - // error processing the response (e.g., authentication succeeded but we didn't get a - // token for some reason) - this._loggedIn = true; - this._storeInfo(); - result = progress.data.Session.SUCCESS; - } else if (xhr.status === 401) { - // If this is Anonymous, somebody gave us the wrong authenticationModel! - result = progress.data.Session.AUTHENTICATION_FAILURE; - } else { - result = progress.data.Session.GENERAL_FAILURE; - } - - this._settlePromise(deferred, result, {"xhr": xhr}); - }; - - - // logout API METHOD -- SOME CONSTRUCTORS OR PROTOTYPES WILL OVERRIDE THIS - progress.data.AuthenticationProvider.prototype.logout = function () { - var deferred = $.Deferred(); - - this._reset(); - deferred.resolve(this, progress.data.Session.SUCCESS, {}); - return deferred.promise(); - }; - - // hasClientCredentials API METHOD -- PROBABLY ONLY OVERRIDDEN BY SSO - progress.data.AuthenticationProvider.prototype.hasClientCredentials = function () { - return this._loggedIn; - }; - - // hasRefreshToken API METHOD -- returns false for all AutghenticationProvider types except SSO, - // which overrides it - progress.data.AuthenticationProvider.prototype.hasRefreshToken = function () { - return false; - }; - - // QUASI-PUBLIC METHOD - - // general-purpose method for opening requests (mainly for jsdo calls) - // This method is not part of the documented API that a developer would - // program against, but it gets used in a validation check by the JSDOSESSION, because the - // JSDOSESSION code expects it to be present. The point here is that if a developer were to - // create their own AuthenticationProvider object, it would need to include this method - // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change - // it to use promises - progress.data.AuthenticationProvider.prototype._openRequestAndAuthorize = function (xhr, - verb, - uri, - async, - callback) { - var errorObject; - - if (this.hasClientCredentials()) { - xhr.open(verb, uri, async); - - // Check out why we do this in _loginProto - xhr.setRequestHeader("Accept", "application/json"); - } else { - // AuthenticationProvider: The AuthenticationProvider is not managing valid credentials. - errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); - } - - callback(errorObject); - }; - - // GENERAL PURPOSE "INTERNAL" METHODS, NOT RELATED TO SPECIFIC API ELEMENTS - // (not documented, intended for use only within the JSDO library) - - // General purpose method for initializing an object - progress.data.AuthenticationProvider.prototype._initialize = function (uriParam, - authModel, - targetURIs) { - var tempURI, - target; - - Object.defineProperty(this, 'uri', - { - get: function () { - return this._uri; - }, - enumerable: true - }); - - Object.defineProperty(this, 'authenticationModel', - { - get: function () { - return this._authenticationModel; - }, - enumerable: true - }); - - - // get rid of trailing '/' because appending service url that starts with '/' - // will cause request failures - if (uriParam[uriParam.length - 1] === "/") { - tempURI = uriParam.substring(0, uriParam.length - 1); - } else { - tempURI = uriParam; - } - - // take the modified authentication uri and prepend it to all of the targets passed - // in. E.g., the targetURIs object will include a "loginURI" property that has the - // uri segment which is to be added to the auth uri for logging in - for (target in targetURIs) { - if (targetURIs.hasOwnProperty(target)) { - this[target] = tempURI + targetURIs[target]; - } - } - - this._authenticationModel = authModel; - this._uri = uriParam; // keep the uri property the same as what was passed in - - this._loggedIn = false; - this._dataKeys = { - uri: ".uri", - loggedIn: ".loggedIn" - }; - - // future: for page refresh -- storeSessionInfo("authenticationModel", authenticationModel); - - if (typeof sessionStorage === "undefined") { - // "AuthenticationProvider: No support for sessionStorage." - throw new Error(progress.data._getMsgText("jsdoMSG126", - "AuthenticationProvider", - "sessionStorage")); - } - // if you switch to a different type of storage, change the error message argument above - this._storage = sessionStorage; - - // maybe should come up with something more intelligent than this - this._storageKey = this._uri; // or name - this._dataKeys.uri = this._storageKey + this._dataKeys.uri; - this._dataKeys.loggedIn = this._storageKey + this._dataKeys.loggedIn; - - if (this._retrieveLoggedIn()) { - this._loggedIn = true; - } - }; - - - // Store data in storage with the uri as the key. setItem() throws. (Should add an - // option for the developer to specify the key) - // a "QuotaExceededError" error if there is insufficient storage space or - // "the user has disabled storage for the site" (Web storage spec at WHATWG) - progress.data.AuthenticationProvider.prototype._storeInfo = function () { - this._storage.setItem(this._dataKeys.uri, JSON.stringify(this._uri)); - this._storage.setItem(this._dataKeys.loggedIn, JSON.stringify(this._loggedIn)); - }; - - // Get a piece of state data from storage. Returns null if the item isn't in storage - progress.data.AuthenticationProvider.prototype._retrieveInfoItem = function (propName) { - var jsonStr = this._storage.getItem(propName), - value = null; - - if (jsonStr !== null) { - try { - value = JSON.parse(jsonStr); - } catch (e) { - value = null; - } - } - return value; - }; - - // Get an AuthenticationProvider's uri from storage - progress.data.AuthenticationProvider.prototype._retrieveURI = function () { - return this._retrieveInfoItem(this._dataKeys.uri); - }; - - // Get an AuthenticationProvider's logon status from storage - progress.data.AuthenticationProvider.prototype._retrieveLoggedIn = function () { - return this._retrieveInfoItem(this._dataKeys.loggedIn); - }; - - // Clear the persistent storage used by an AuthenticationProvider - progress.data.AuthenticationProvider.prototype._clearInfo = function (info) { - this._storage.removeItem(this._dataKeys.uri); - this._storage.removeItem(this._dataKeys.loggedIn); - }; - - // Put the internal state back to where it is when the constructor finishes - // running (so the authentication model and uri are not changed, but other data is reset. - // and storage is cleared out) - progress.data.AuthenticationProvider.prototype._reset = function () { - this._clearInfo(); - this._loggedIn = false; - }; - - - // General purpose utility method, no overrides expected - progress.data.AuthenticationProvider.prototype._settlePromise = function (deferred, result, info) { - if (result === progress.data.Session.SUCCESS) { - deferred.resolve(this, result, info); - } else { - deferred.reject(this, result, info); - } - }; - - // General purpose utility method, no overrides expected - progress.data.AuthenticationProvider.prototype._checkStringArg = function (fnName, - argToCheck, - argPosition, - argName) { - // TODO: ? distinguish between undefined (so we can give developer a clue that they - // may be missing a property) and defined but wrong type - if (typeof argToCheck !== "string") { - // AuthenticationProvider: Argument {param-position} must be of type string in {fnName} call. - throw new Error(progress.data._getMsgText( - "jsdoMSG121", - "AuthenticationProvider", - argPosition, - "string", - fnName - )); - } else if (argToCheck.length === 0) { - // AuthenticationProvider: {param-name} cannot be an empty string. - throw new Error(progress.data._getMsgText( - "jsdoMSG501", - "AuthenticationProvider", - argName, - fnName - )); - } - }; - - - // "STATIC" PROPERTIES AND METHODS -- not on the prototype -- you cannot access these through an - // object created by "new" --- they are properties of the AuthenticationProvider constructor function - - // Takes an XHR as an input. If the xhr status is 401 (Unauthorized), determines whether - // the auth failure was due to an expired token. Returns progress.data.Session.EXPIRED_TOKEN - // if it was, progress.data.Session.AUTHENTICATION_FAILURE if it wasn't, null if the xhr status wasn't 401 - progress.data.AuthenticationProvider._getAuthFailureReason = function (xhr) { - var contentType, - jsonObject, - result = progress.data.Session.AUTHENTICATION_FAILURE; - - if (xhr.status === 401) { - contentType = xhr.getResponseHeader("Content-Type"); - if (contentType && (contentType.indexOf("application/json") > -1) && xhr.responseText) { - jsonObject = JSON.parse(xhr.responseText); - if (jsonObject.error === "sso.token.expired_token") { - result = progress.data.Session.EXPIRED_TOKEN; - } - } - } else { - result = null; - } - return result; - }; - - Object.defineProperty(progress.data.AuthenticationProvider, '_homeLoginURIBase', { - value: "/static/home.html", - enumerable: true - }); - Object.defineProperty(progress.data.AuthenticationProvider, '_springLoginURIBase', { - value: "/static/auth/j_spring_security_check", - enumerable: true - }); - Object.defineProperty(progress.data.AuthenticationProvider, '_springLogoutURIBase', { - value: "/static/auth/j_spring_security_logout", - enumerable: true - }); - Object.defineProperty(progress.data.AuthenticationProvider, '_springFormTokenLoginURIBase', { - value: progress.data.AuthenticationProvider._springLoginURIBase + "?OECP=yes", - enumerable: true - }); - Object.defineProperty(progress.data.AuthenticationProvider, '_springFormTokenRefreshURIBase', { - value: "/static/auth/token?op=refresh", - enumerable: true - }); - -}()); - -//# sourceURL=progress.jsdo.js -/* -progress.auth.basic.js Version: 4.4.0-3 - -Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - "use strict"; // note that this makes JSLint complain if you use arguments[x] - - /*global progress : true*/ - /*global $ : false, storage, XMLHttpRequest, msg, btoa*/ - - progress.data.AuthenticationProviderBasic = function (uri) { - var defaultiOSBasicAuthTimeout, // TO DO: need to implement the use of this - userName = null, - password = null, - fn; - - // process constructor arguments, etc. - this._initialize(uri, progress.data.Session.AUTH_TYPE_BASIC, - {"_loginURI": progress.data.AuthenticationProvider._homeLoginURIBase}); - - // PRIVATE FUNCTIONS - - // from http://coderseye.com/2007/how-to-do-http-basic-auth-in-ajax.html - function make_basic_auth_header(user, pw) { - var tok = user + ':' + pw, - hash = btoa(tok); - return "Basic " + hash; - } - - // "INTERNAL" METHODS - // Override the protoype's method but call it from within the override - // (Define the override here in the constructor so it has access to instance variables) - this._reset = function () { - userName = null; - password = null; - progress.data.AuthenticationProviderBasic.prototype._reset.apply(this); - }; - - // Override the protoype's method (this method does not invoke the prototype's copy) - // (Define the override here in the constructor so it has access to instance variables) - this._openLoginRequest = function (xhr, uri) { - var auth; - - xhr.open("GET", uri, true); // but see comments below inside the "if userName" - // may have to go with that approach - - if (userName) { - - // set Authorization header - auth = make_basic_auth_header(userName, password); - xhr.setRequestHeader('Authorization', auth); - } - - progress.data.Session._setNoCacheHeaders(xhr); - }; - - // Override the protoype's method but call it from within the override - // (Define the override here in the constructor so it has access to instance variables) - this._processLoginResult = function _basic_processLoginResult(xhr, deferred) { - progress.data.AuthenticationProviderBasic.prototype._processLoginResult.apply( - this, - [xhr, deferred] - ); - if (!this._loggedIn) { - // login failed, clear the credentials - userName = null; - password = null; - } - }; - - // Override the protoype's method (this method does not invoke the prototype's copy, but - // calls a prototype general-purpose login method) - // (Define the override here in the constructor so it has access to instance variables) - this.login = function (userNameParam, passwordParam) { - // these throw if the check fails (may want to do something more elegant) - this._checkStringArg("login", userNameParam, 1, "userName"); - this._checkStringArg("login", passwordParam, 2, "password"); - - userName = userNameParam; - password = passwordParam; - return this._loginProto(); - }; - - // Override the protoype's method (this method does not invoke the prototype's copy) - // (Define the override here in the constructor so it has access to instance variables) - // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change - // it to use promises - this._openRequestAndAuthorize = function (xhr, verb, uri, async, callback) { - var auth, - errorObject; - - if (this.hasClientCredentials()) { - - xhr.open(verb, uri, async); // but see comments below inside the "if userName" - // may have to go with that approach - - if (userName) { - - // set Authorization header - auth = make_basic_auth_header(userName, password); - xhr.setRequestHeader('Authorization', auth); - } - - progress.data.Session._setNoCacheHeaders(xhr); - } else { - // AuthenticationProvider: The AuthenticationProvider is not managing valid credentials. - errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); - } - - callback(errorObject); - }; - }; - - - // Give this constructor the prototype from the "base" AuthenticationProvider - // Do this indirectly by way of an intermediate object so changes to the prototype ("method overrides") - // don't affect other types of AuthenticationProviders that use the prototype) - function BasicProxy() {} - BasicProxy.prototype = progress.data.AuthenticationProvider.prototype; - progress.data.AuthenticationProviderBasic.prototype = new BasicProxy(); - - // Reset the prototype's constructor property so it points to AuthenticationProviderForm rather than - // the one that it just inherited (this is pretty much irrelevant though - the correct constructor - // will get called regardless) - progress.data.AuthenticationProviderBasic.prototype.constructor = - progress.data.AuthenticationProviderBasic; - - - // OVERRIDE METHODS ON PROTOTYPE IF NECESSARY AND POSSIBLE - // (SOME METHODS ARE OVERRIDDEN IN THE CONSTRUCTOR BECAUSE THEY NEED ACCESS TO INSTANCE VARIABLES) - - // NOTE: There are no overrides of the following methods (either here or in the constructor). - // This object uses these methods from the original prototype(i.e., the implementations from the - // AuthenticationProvider object): - // logout (API method) - // hasClientCredentials (API method) - -}()); -//# sourceURL=progress.jsdo.js -/* -progress.auth.form.js Version: 4.4.0-3 - -Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - "use strict"; // note that this makes JSLint complain if you use arguments[x] - - /*global progress : true*/ - /*global $ : false, XMLHttpRequest*/ - - var fn; - - progress.data.AuthenticationProviderForm = function (uri) { - - // PROCESS CONSTRUCTOR ARGUMENTS, CREATE API PROPERTIES, ETC. - this._initialize(uri, progress.data.Session.AUTH_TYPE_FORM, - {"_loginURI": progress.data.AuthenticationProvider._springLoginURIBase, - "_logoutURI": progress.data.AuthenticationProvider._springLogoutURIBase - }); - }; - - // Start by giving this constructor the prototype from the "base" AuthenticationProvider - // Do this indirectly by way of an intermediate object so changes to the prototype ("method overrides") - // don't affect other types of AuthenticationProviders that use the prototype) - function FormProxy() {} - FormProxy.prototype = progress.data.AuthenticationProvider.prototype; - progress.data.AuthenticationProviderForm.prototype = - new FormProxy(); - - // Reset the prototype's constructor property so it points to AuthenticationProviderForm rather than - // the one that it just inherited (this is pretty much irrelevant though - the correct constructor - // will get called regardless) - progress.data.AuthenticationProviderForm.prototype.constructor = - progress.data.AuthenticationProviderForm; - - - // OVERRIDE THE "BASE" AuthenticationProvider PROTOYPE METHODS WHERE NECESSARY - - // All of the methods defined here as part of the AuthenticationProviderForm prototype (instead - // of in the AuthenticationProviderForm constructor) can be inherited by the AuthenticationProviderSSO - // prototype without incurring the overhead of creating a full-blown instance of - // AuthenticationProviderForm to serve as the prototype for the SSO constructor. - // Note: if it turns out that any of the methods defined this way need access to internal variables - // of an AuthenticationProviderForm object, they'll need to be moved out of here. - - // NOTE: There are no overrides of the following methods (either here or in the constructor). - // This object uses these methods from the original prototype(i.e., the implementations from the - // AuthenticationProvider object): - // _reset (general-purpose helper) - // hasClientCredentials (API method) - // _processLoginResult (API helper method) - - - // login API METHOD AND "HELPERS" - progress.data.AuthenticationProviderForm.prototype.login = function (userNameParam, passwordParam) { - var deferred = $.Deferred(), - xhr, - that = this; - - // these throw if the check fails (may want to do something more elegant) - this._checkStringArg("login", userNameParam, 1, "userName"); - this._checkStringArg("login", passwordParam, 2, "password"); - - return this._loginProto("j_username=" + encodeURIComponent(userNameParam) + - "&j_password=" + encodeURIComponent(passwordParam) + "&submit=Submit"); - }; - - // login helper - // Override the protoype's method (this method does not invoke the prototype's copy) - // By defining this here, we can have the SSO AuthenticationProvider use it without - // incurring the overhead of creating a Form instance as the prototype for the SSO constructor - progress.data.AuthenticationProviderForm.prototype._openLoginRequest = function (xhr, uri) { - - xhr.open('POST', uri, true); - - xhr.setRequestHeader("Cache-Control", "max-age=0"); - xhr.setRequestHeader("Pragma", "no-cache"); - xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); - - xhr.withCredentials = true; - - }; - - // logout API METHOD AND "HELPERS" - // Override the prototype method and do not call it because the Anonymous AuthenticationProvider - // doesn't make a server call for logout - // (But this method does do what the SSO AuthenticationProvider needs, so keep it on - // the Form prototype if possible) - progress.data.AuthenticationProviderForm.prototype.logout = function () { - var deferred = $.Deferred(), - xhr, - that = this; - - if (!this._loggedIn) { - // logout is regarded as a success if the AuthenticationProvider isn't logged in - deferred.resolve(this, progress.data.Session.SUCCESS, {}); - } else { - xhr = new XMLHttpRequest(); - this._openLogoutRequest(xhr); - - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - // process the response from the Web application - that._processLogoutResult(xhr, deferred); - } - }; - - xhr.send(); - } - - // Unconditionally reset --- even if the actual server request fails, we still want - // to reset this AuthenticationProvider so it can try a login if desired. - // We also reset even in the case where we're not logged in, just in case. - // (In the future we can add a parameter that controls whether the reinit is unconditional, - // if the developer wants to log out of the token server session but contnue to use the token) - this._reset(); - return deferred.promise(); - }; - - // logout helper (there is no version defined in the original protoype) - progress.data.AuthenticationProviderForm.prototype._openLogoutRequest = function (xhr) { - xhr.open('GET', this._logoutURI, true); - xhr.setRequestHeader("Cache-Control", "max-age=0"); - xhr.withCredentials = true; - xhr.setRequestHeader("Accept", "application/json"); - }; - - // logout helper (there is no version defined in the original protoype) - progress.data.AuthenticationProviderForm.prototype._processLogoutResult = function (xhr, deferred) { - var result; - - if (xhr.status === 200) { - result = progress.data.Session.SUCCESS; - } else if (xhr.status === 401) { - // treat this as a success because the most likely cause is that the session expired - // (Note that an 11.7 OE PAS Web application will return a 200 if we log out with - // an expired JSESSIONID, so this code may not be executed anyway) - result = progress.data.Session.SUCCESS; - } else { - result = progress.data.Session.GENERAL_FAILURE; - } - - this._settlePromise(deferred, result, {"xhr": xhr}); - - }; - - // GENERAL PURPOSE METHOD FOR OPENING REQUESTS (MAINLY FOR JSDO CALLS) - // Override the protoype's method but call it from within the override - // Since the override is being put into the constructor's prototype, and - // since it calls the overridden method which had originally been in the prototype, - // we add a "_super" property to the overriding method so it can still access the original method - // (We could just call that method directly in here like this: - // progress.data.AuthenticationProviderProto.prototype._openRequestAndAuthorize - // but if we ever change the place where we get the initial protoype for - // AuthenticationProviderForm from, we would need to remember to change that here. - // The use of the _super property will handle that automatically, plus it was more fun - // to do it this way) - // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change - // it to use promises - fn = progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize; - progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize = - function (xhr, verb, uri, async, callback) { - - function afterSuper(errorObject) { - xhr.withCredentials = true; - callback(errorObject); - } - - try { - progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize._super.apply( - this, - [xhr, verb, uri, async, afterSuper] - ); - } catch (e) { - callback(e); - } - }; - progress.data.AuthenticationProviderForm.prototype._openRequestAndAuthorize._super = fn; - - -}()); -//# sourceURL=progress.jsdo.js -/* -progress.auth.sso.js Version: 4.4.0-3 - -Copyright (c) 2016-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -(function () { - - "use strict"; // note that this makes JSLint complain if you use arguments[x] - - /*global progress : true*/ - /*global $ : false */ - - var fn; - -// ADD AN OPTIONS PARAM THAT CAN INCLUDE A NAME FOR PAGE REFRESH? - progress.data.AuthenticationProviderSSO = function (uri) { - var that = this, - // SSO specific - _automaticTokenRefresh, - temp, - ssoTokenInfo = null, - tokenDataKeys = { // SSO specific - token: ".access_token", - refreshToken: ".refresh_token", - tokenType: ".token_type", - expiration: ".expires_in", - accessTokenExpiration: ".accessTokenExpiration" - }; - - // PRIVATE FUNCTIONS - // (The constructor uses local variables and functions mainly to try to protect the token - // information as much as possible. A few could probably be defined as properties/methods, but - // there's currently no need for that because the AuthenticationProvider API has no objects - // that inherit from AuthenticationProviderSSO.) - - // Store the given token with the uri as the key. setItem() throws - // a "QuotaExceededError" error if there is insufficient storage space or - // "the user has disabled storage for the site" (Web storage spec at WHATWG) - function storeTokenInfo(info) { - var date, - accessTokenExpiration; - - if (info.access_token.length) { - that._storage.setItem(tokenDataKeys.token, JSON.stringify(info.access_token)); - } - if (info.refresh_token.length) { - that._storage.setItem(tokenDataKeys.refreshToken, JSON.stringify(info.refresh_token)); - // The time given for the access token's expiration is in seconds. We transform it - // into milliseconds and add it to date.getTime() for a more standard format. - date = new Date(); - // This should probably be renamed accessTokenRefreshThreshold - accessTokenExpiration = date.getTime() + (info.expires_in * 1000 * 0.75); - that._storage.setItem(tokenDataKeys.accessTokenExpiration, JSON.stringify(accessTokenExpiration)); - } else { - // if there is no refresh token, remove any existing one. This handles the case where - // we got a new token via refresh, but now we're not being given any more refresh tokens - that._storage.removeItem(tokenDataKeys.refreshToken); - that._storage.removeItem(tokenDataKeys.accessTokenExpiration); - } - that._storage.setItem(tokenDataKeys.tokenType, JSON.stringify(info.token_type)); - that._storage.setItem(tokenDataKeys.expiration, JSON.stringify(info.expires_in)); - } - - // get one of the pieces of data related to tokens from storage (could be the token itself, or - // the refresh token, expiration info, etc.). Returns null if the item isn't in storage - function retrieveTokenProperty(propName) { - var jsonStr = that._storage.getItem(propName), - value = null; - - if (jsonStr !== null) { - try { - value = JSON.parse(jsonStr); - } catch (e) { - value = null; - } - } - return value; - } - - function retrieveToken() { - return retrieveTokenProperty(tokenDataKeys.token); - } - - function retrieveRefreshToken() { - return retrieveTokenProperty(tokenDataKeys.refreshToken); - } - - function retrieveAccessTokenExpiration() { - return retrieveTokenProperty(tokenDataKeys.accessTokenExpiration); - } - - function retrieveTokenType() { - return retrieveTokenProperty(tokenDataKeys.tokenType); - } - - // This is going to be hardcoded for now. This can very - // possibly change in the future if we decide to expose - // the token to the user. - function getToken() { - return retrieveToken(); - } - - function retrieveExpiration() { - return retrieveTokenProperty(tokenDataKeys.expiration); - } - - function clearTokenInfo(info) { - that._storage.removeItem(tokenDataKeys.token); - that._storage.removeItem(tokenDataKeys.refreshToken); - that._storage.removeItem(tokenDataKeys.tokenType); - that._storage.removeItem(tokenDataKeys.expiration); - that._storage.removeItem(tokenDataKeys.accessTokenExpiration); - } - - // function is SSO specific - function openRefreshRequest(xhr) { - xhr.open('POST', that._refreshURI, true); - xhr.setRequestHeader("Cache-Control", "max-age=0"); - xhr.withCredentials = true; - xhr.setRequestHeader("Content-Type", "application/json"); - xhr.setRequestHeader("Accept", "application/json"); - } - - // function is SSO specific - function processRefreshResult(xhr, deferred) { - var errorObject, - result, - ssoTokenJSON; - - if (xhr.status === 200) { - // get token and store it; if that goes well, resolve the promise, otherwise reject it - try { - ssoTokenInfo = JSON.parse(xhr.responseText); - - if (ssoTokenInfo.access_token) { - storeTokenInfo(ssoTokenInfo); - // got the token info, its access_token has a value, and storeTokenInfo() - // didn't thrown an error, so call this a success - result = progress.data.Session.SUCCESS; - } else { - result = progress.data.Session.GENERAL_FAILURE; - // {1}: Unexpected error calling refresh: {error-string} - // ( No token returned from server) - errorObject = new Error(progress.data._getMsgText( - "jsdoMSG049", - "AuthenticationProvider", - "refresh", - progress.data._getMsgText("jsdoMSG050") - )); - } - } catch (ex) { - result = progress.data.Session.GENERAL_FAILURE; - // {1}: Unexpected error calling refresh: {error-string} - // (error could be thrown from storeTokenInfo when it calls setItem()) - errorObject = new Error(progress.data._getMsgText( - "jsdoMSG049", - "AuthenticationProvider", - "refresh", - ex.message - )); - } - } else if (xhr.status === 401) { - that._reset(); // treat authentication failure as the equivalent of a logout - result = progress.data.Session.AUTHENTICATION_FAILURE; - } else { - result = progress.data.Session.GENERAL_FAILURE; - } - - that._settlePromise(deferred, result, {"xhr": xhr, - "errorObject": errorObject}); // OK if undefined - } - - - this._processLoginResult = function (xhr, deferred) { - var errorObject, - result, - ssoTokenJSON; - - if (xhr.status === 200) { - // Need to set loggedIn now so we can call logout from here if there's an - // error processing the response (e.g., authentication succeeded but we didn't get a - // token for some reason) - this._loggedIn = true; - - // get token and store it; if that goes well, resolve the promise, otherwise reject it - try { - ssoTokenInfo = JSON.parse(xhr.responseText); - - if (ssoTokenInfo.access_token) { - storeTokenInfo(ssoTokenInfo); - // got the token info, its access_token has a value, and storeTokenInfo() - // didn't throw an error, so call this a success - result = progress.data.Session.SUCCESS; - } else { - result = progress.data.Session.GENERAL_FAILURE; - // {1}: Unexpected error calling login: {error-string} - // ( No token returned from server) - errorObject = new Error(progress.data._getMsgText( - "jsdoMSG049", - "AuthenticationProvider", - "login", - progress.data._getMsgText("jsdoMSG050") - )); - } - } catch (ex) { - result = progress.data.Session.GENERAL_FAILURE; - // {1}: Unexpected error calling login: {error-string} - // (error could be thrown from storeTokenInfo when it calls setItem()) - errorObject = new Error(progress.data._getMsgText( - "jsdoMSG049", - "AuthenticationProvider", - "login", - ex.message - )); - } - - // log out if there was an error processing the response so the app can try to log in again - if (result !== progress.data.Session.SUCCESS) { - // call logout, but ignore its outcome -- just tell caller that login failed - this.logout() - .always(function (authProv) { - authProv._settlePromise(deferred, result, {"xhr": xhr, - "errorObject": errorObject}); - }); - return; // so we don't execute the reject below, which could invoke the fail handler - // before we're done with the logout - } - - } else if (xhr.status === 401) { - result = progress.data.Session.AUTHENTICATION_FAILURE; - } else { - result = progress.data.Session.GENERAL_FAILURE; - } - - this._settlePromise(deferred, result, {"xhr": xhr}); - }; - - - // Override the protoype's method but call it from within the override. (Define the override - // here in the constructor so it has access to the internal function and variable) - this._reset = function () { - progress.data.AuthenticationProviderSSO.prototype._reset.apply(this); - clearTokenInfo(); - ssoTokenInfo = null; - }; - - - // Override the protoype's method but call it from within the override. (Define the override - // here in the constructor so it has access to the internal function getToken() ) - // TODO: This method uses a callback, primarily to avoid breaking tdriver tests. We should change - // it to use promises - this._openRequestAndAuthorize = function (xhr, - verb, - uri, - async, - callback) { - var that = this, - date, - errorObject; - - function afterRefreshCheck(provider, result, info) { - // if refresh failed because of auth failure, we will have gotten rid of the - // token and reset the auth provider - if (result === progress.data.Session.AUTHENTICATION_FAILURE) { - callback(new Error(progress.data._getMsgText("jsdoMSG060"))); - } else { - // We've done the refresh check (and possible refresh) for SSO, now execute - // the base _openRequest... method, which does common things for Form-based - progress.data.AuthenticationProviderSSO.prototype._openRequestAndAuthorize.apply( - that, - [xhr, verb, uri, async, function (errorObject) { - if (!errorObject) { - xhr.setRequestHeader('Authorization', "oecp " + getToken()); - } - callback(errorObject); - }] - ); - } - } - - if (this.hasClientCredentials()) { - // Every token given has an expiration "hint". If the token's lifespan - // is close to or past that limit, then a refresh is done. - // No matter what the outcome of the refresh, keep in mind we always - // send the original request. - date = new Date(); - if (this.automaticTokenRefresh && - this.hasRefreshToken() && - date.getTime() > retrieveAccessTokenExpiration()) { - try { - this.refresh() - .always(function (provider, result, info) { - afterRefreshCheck(provider, result, info); - }); - } catch (e) { - callback(e); - } - } else { - afterRefreshCheck(this, progress.data.Session.SUCCESS, null); - } - } else { - // This message is SSO specific, unless we can come up with a more general message - // JSDOSession: The AuthenticationProvider needs to be managing a valid token. - errorObject = new Error(progress.data._getMsgText("jsdoMSG125", "AuthenticationProvider")); - callback(errorObject); - } - }; - - - // API METHODS - - // override the prototype's hasClientCredentials method - this.hasClientCredentials = function () { - return (retrieveToken() === null ? false : true); - }; - - - this.refresh = function () { - var deferred = $.Deferred(), - xhr; - - if (!this._loggedIn) { - // "The refresh method was not executed because the AuthenticationProvider is not logged in." - throw new Error(progress.data._getMsgText("jsdoMSG053", "AuthenticationProvider", "refresh")); - } - - if (!this.hasRefreshToken()) { - // "Token refresh was not executed because the AuthenticationProvider does not have a - // refresh token." - throw new Error(progress.data._getMsgText("jsdoMSG054", "AuthenticationProvider")); - } - - xhr = new XMLHttpRequest(); - openRefreshRequest(xhr); - - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - // process the response from the Web application - processRefreshResult(xhr, deferred); - } - }; - - xhr.send('{"token_type":"' + retrieveTokenType() + '","refresh_token":"' + - retrieveRefreshToken() + '"}'); - return deferred.promise(); - }; - - - this.hasRefreshToken = function () { - return (retrieveRefreshToken() === null ? false : true); - }; - - - // PROCESS CONSTRUCTOR ARGUMENTS, CREATE API PROPERTIES, ETC. - this._initialize(uri, - progress.data.Session.AUTH_TYPE_FORM_SSO, - {"_loginURI": progress.data.AuthenticationProvider._springFormTokenLoginURIBase, - "_logoutURI": progress.data.AuthenticationProvider._springLogoutURIBase, - "_refreshURI": progress.data.AuthenticationProvider._springFormTokenRefreshURIBase - }); - - // in addition to the standard AuthenticationProvider properties, an SSO provider also has a property - // to control automatic token refresh (it's enabled by default on the assumption that developers - // will usually want this) - _automaticTokenRefresh = true; - Object.defineProperty(this, 'automaticTokenRefresh', - { - get: function () { - return _automaticTokenRefresh; - }, - set: function (value) { - if (value === true || value === false) { - _automaticTokenRefresh = value; - } else { - throw new Error(progress.data._getMsgText("jsdoMSG061", - "AuthenticationProvider", - "automaticTokenRefresh")); - } - }, - enumerable: true - }); - - // add the automaticTokenRefresh key to the base class's list of data keys - this._dataKeys.automaticTokenRefresh = this._storageKey + ".automaticTokenRefresh"; - // set it from storage, if it's in storage - temp = this._retrieveInfoItem(this._dataKeys.automaticTokenRefresh); - if (temp === false) { - _automaticTokenRefresh = false; - } - - // We're currently storing the token in storage with the - // uri as the key. This is subject to change later. - tokenDataKeys.token = this._storageKey + tokenDataKeys.token; - tokenDataKeys.refreshToken = this._storageKey + tokenDataKeys.refreshToken; - tokenDataKeys.tokenType = this._storageKey + tokenDataKeys.tokenType; - tokenDataKeys.expiration = this._storageKey + tokenDataKeys.expiration; - tokenDataKeys.accessTokenExpiration = this._storageKey + tokenDataKeys.accessTokenExpiration; - - // NOTE: we rely on the prototype's logic to set this._loggedIn. An alternative could be to - // use the presence of a token to determine that, but it's conceivable that we could be - // logged in but for some reason not have a token (e.g., a token expired, or we logged in - // but the authentication server did not return a token) - if (retrieveToken()) { - this._loggedIn = true; - } - - // END OF CONSTRUCTOR PROCESSING - - }; - // END OF AuthenticationProviderSSO CONSTRUCTOR - - // NOTE: This is used only for the SSO authentication. - // Define the prototype as an instance of an AuthenticationProviderForm object - function SSOProxy() {} - SSOProxy.prototype = progress.data.AuthenticationProviderForm.prototype; - progress.data.AuthenticationProviderSSO.prototype = - new SSOProxy(); - - // But reset the constructor back to the SSO constructor (this is pretty much irrelevant, - // though. The correct constructor would be called anyway. It's mainly for the sake of anyone - // wanting to see what the constructor of an object is (maybe a framework) - progress.data.AuthenticationProviderSSO.prototype.constructor = - progress.data.AuthenticationProviderSSO; - - // override the base AuthenticationProvider _storeInfo and _clearinfo, but keep refs so they - // can be invoked within the overrides - fn = progress.data.AuthenticationProviderSSO.prototype._storeInfo; - progress.data.AuthenticationProviderSSO.prototype._storeInfo = - function () { - progress.data.AuthenticationProviderSSO.prototype._storeInfo._super.apply(this); - this._storage.setItem(this._dataKeys.automaticTokenRefresh, - JSON.stringify(this._automaticTokenRefresh)); - }; - progress.data.AuthenticationProviderSSO.prototype._storeInfo._super = fn; - - fn = progress.data.AuthenticationProviderSSO.prototype._clearInfo; - progress.data.AuthenticationProviderSSO.prototype._clearInfo = - function () { - progress.data.AuthenticationProviderSSO.prototype._clearInfo._super.apply(this); - this._storage.removeItem(this._dataKeys.automaticTokenRefresh); - }; - progress.data.AuthenticationProviderSSO.prototype._clearInfo._super = fn; - - - - // NOTE: There are no overrides of the following methods (either here or in the constructor). - // This object uses these methods from the original prototype(i.e., the implementations from the - // Auth...Form object) because for an OE SSO token server, the login/logout model is Form (the - // only difference is the use of a special URI query string in the login (see the call to - // initialize() in the SSO constructor (above)): - // login (API method) - // _openLoginRequest (API helper method) - // logout (API method) - // _openLogoutRequest (API helper method) - // _processLogoutResult (API helper method) - - // NOTE: All overrides are implemented in the constructor (rather than adding them to the prototype) - // because they need access to variables and/or functions that are defined in the constructor - // (in an attempt to protect the token info somewhat) - -}()); - - -/* -progress.data.kendo.js Version: 4.4.0-01 - -Copyright (c) 2015-2017 Progress Software Corporation and/or its subsidiaries or affiliates. - -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. - - */ - -/*global jQuery, kendo, progress*/ -/*jslint nomen: true*/ -/*jslint vars: false*/ -(function () { - - // "use strict"; - - var JSDODataReader, JSDOTransport, JSDOObservable = new kendo.Observable(); - - function initializeJSDO(transport, options) { - var jsdo, resourceName; - - if (options.jsdo instanceof progress.data.JSDO) { - jsdo = options.jsdo; - } else if (typeof (options.jsdo) === "string") { - // Create a new JSDO instance using the specified configuration - resourceName = options.jsdo; - - // Create JSDO - jsdo = new progress.data.JSDO({ name: resourceName }); - } else { - throw new Error("JSDO: jsdo property must be either a JSDO instance or a string."); - } - - if (transport.tableRef === undefined && jsdo._defaultTableRef) { - transport.tableRef = jsdo._defaultTableRef._name; - } - if (transport.tableRef === undefined) { - throw new Error("JSDO: A tableRef must be specified when using a multi-table DataSet."); - } else if (jsdo[transport.tableRef] === undefined) { - throw new Error("JSDO: tableRef '" + transport.tableRef + "' is not present in JSDO definition."); - } - - return jsdo; - } - - - - // This define functions to read the data object from the JSDO DataSource - JSDODataReader = kendo.data.readers.json.extend({ - init: function (arg1) { - // init function - var event = {}, - transport; - - // Query the transport object to obtain a transport reference - JSDOObservable.trigger("info", event); - transport = this.transport = event.sender._events.info.transport; - - kendo.data.readers.json.fn.init.call(this, arg1); - - // Overrides model property after init.call - // because if model is defined at the object level init.call removes it - this.model = kendo.data.Model.define({ - init: function (data) { - var record; - if (!data || jQuery.isEmptyObject(data)) { - data = transport._getInitialValues(); - } - record = transport._convertDataTypes(data); - transport.jsdo._deleteProdsProperties(record, true); - kendo.data.Model.fn.init.call(this, record); - }, - id: "_id", - fields: transport._getModel() - }); - }, - total: function (data) { - return data.total || (data.data ? data.data.length : data.length); - }, - data: function (data) { - return data.data || data; - } - }); - - // This define transport for JSDO DataSource providing implementation for: - // create, read, update, destroy, submit - JSDOTransport = kendo.data.RemoteTransport.extend({ - init: function (options) { - var transport = this, - fnName; - - if (options.tableRef !== undefined) { - transport.tableRef = options.tableRef; - } - transport.jsdo = initializeJSDO(transport, options); - transport._initFromServer = false; - transport.autoSave = options.autoSave !== undefined ? options.autoSave : true; - transport.readLocal = options.readLocal !== undefined ? options.readLocal : false; - transport.countFnName = options.countFnName; - transport.useArrays = options.useArrays !== undefined ? options.useArrays : false; - - if (transport.countFnName !== undefined) { - if (typeof (transport.jsdo[transport.countFnName]) !== "function") { - throw new Error("Invoke operation '" + - transport.countFnName + "' for countFnName is not defined."); - } - } else if (transport.jsdo._resource.generic.count !== undefined) { - for (fnName in transport.jsdo._resource.fn) { - if (transport.jsdo._resource.fn.hasOwnProperty(fnName)) { - if (transport.jsdo._resource.generic.count === transport.jsdo._resource.fn[fnName]["function"]) { - transport.countFnName = fnName; - break; - } - } - } - } - - // Define "info" event to return transport object to reader - JSDOObservable.one("info", function (e) { - e.sender._events.info.transport = transport; - }); - - transport._initConvertTypes(); - - kendo.data.RemoteTransport.fn.init.call(this, options); - }, - _initConvertTypes: function () { - // _initConvertTypes: - // Initializes transport._convertTypes to indicate whether a conversion of the data is needed - // when it is passed to Kendo UI. - // This operation is currently only needed for date fields that are stored as strings. - // Sets array _dateFields to the fields of date fields to convert. - var transport = this, - i, - schema, - fieldName, - dateFields = [], - arrayFields = [], - convertDateFields = false; - - transport._convertTypes = false; - - schema = transport.jsdo[transport.tableRef].getSchema(); - for (i = 0; i < schema.length; i += 1) { - fieldName = schema[i].name; - if (fieldName.length > 0 && fieldName.charAt(0) !== "_") { - if (schema[i].type === "string" && - schema[i].format && - (schema[i].format.indexOf("date") !== -1)) { - dateFields.push(fieldName); - if (!convertDateFields) { - convertDateFields = true; - } - } else if (!transport.useArrays && schema[i].type === "array") { - arrayFields.push(fieldName); - if (!convertDateFields && schema[i].ablType && - schema[i].ablType.indexOf("DATE") === 0) { - convertDateFields = true; - } - } - } - } - - if (dateFields.length > 0 || arrayFields.length > 0) { - transport._convertTypes = true; - // _convertFields: Object containing arrays for each data type to convert - transport._convertFields = {}; - transport._convertFields._arrayFields = []; - transport._convertFields._dateFields = []; - } - if (dateFields.length > 0) { - transport._convertFields._dateFields = dateFields; - } - if (convertDateFields) { - transport._convertFields._datePattern = new RegExp("^([0-9]+)?-([0-9]{2})?-([0-9]{2})?$"); - transport._convertFields._dateTimePattern = new RegExp( - "^([0-9]+)?-([0-9]{2})?-([0-9]{2})?" + - "T([0-9]{2})?:([0-9]{2})?:([0-9]{2})?.([0-9]{3})?$" - ); - } - if (arrayFields.length > 0) { - transport._convertFields._arrayFields = arrayFields; - } - }, - _convertStringToDate: function (data, fieldName, targetFieldName) { - var transport = this, - array, - ablType, - orig; - - if (!targetFieldName) { - targetFieldName = fieldName; - } - // Check if string is -- - array = transport._convertFields._datePattern.exec(data[targetFieldName]) || []; - if (array.length > 0) { - data[targetFieldName] = new Date(parseInt(array[1], 10), - parseInt(array[2], 10) - 1, - parseInt(array[3], 10)); - } else { - ablType = transport.jsdo[transport.tableRef]._fields[fieldName.toLowerCase()].ablType; - if (ablType === "DATETIME") { - array = transport._convertFields._dateTimePattern.exec(data[targetFieldName]) || []; - if (array.length > 0) { - // Convert date to local time zone - data[targetFieldName] = new Date(parseInt(array[1], 10), - parseInt(array[2], 10) - 1, - parseInt(array[3], 10), - parseInt(array[4], 10), - parseInt(array[5], 10), - parseInt(array[6], 10), - parseInt(array[7], 10)); - } - } - - // Check to see if it was converted - if (typeof (data[targetFieldName]) === "string") { - orig = data[targetFieldName]; - try { - data[targetFieldName] = new Date(data[targetFieldName]); - } - catch (e) { - // Conversion to a date object was not successful - data[targetFieldName] = orig; - console.log(msg.getMsgText("jsdoMSG000", - "_convertStringToDate() could not convert to date object: " + orig)); - } - } - } - }, - _convertDataTypes: function (data) { - // _convertDataTypes: - // Converts data types in the specified data record. - // Data record could come from the JSDO or from the Kendo UI DataSource. - // Returns a reference to the record. - // Returns a copy when useArrays is undefined or false. - var transport = this, - i, - k, - fieldName, - schemaInfo, - prefixElement, - elementName, - copy; - - if (!transport.useArrays && transport._convertTypes && (transport._convertFields._arrayFields.length > 0)) { - copy = {}; - transport.jsdo._copyRecord(transport.jsdo._buffers[transport.tableRef], data, copy); - data = copy; - } - - if (!transport._convertTypes) { - return data; - } - - for (k = 0; k < transport._convertFields._arrayFields.length; k += 1) { - fieldName = transport._convertFields._arrayFields[k]; - if (data[fieldName]) { - schemaInfo = transport.jsdo[transport.tableRef]._fields[fieldName.toLowerCase()]; - prefixElement = transport.jsdo._getArrayField(fieldName); - for (i = 0; i < schemaInfo.maxItems; i += 1) { - // ABL arrays are 1-based - elementName = prefixElement.name + (i + 1); - - if (!transport.jsdo[transport.tableRef]._fields[elementName.toLowerCase()]) { - // Skip element if a field with the same name exists - // Extract value from array field into individual field - // Array is removed later - data[elementName] = data[fieldName][i]; - - // Convert string DATE fields to JS DATE - if ((schemaInfo.ablType) && (schemaInfo.ablType.indexOf("DATE") === 0) && (typeof (data[elementName]) === "string")) { - transport._convertStringToDate(data, fieldName, elementName); - } - } - } - if (!transport.useArrays) { - delete data[fieldName]; - } - } - } - - for (k = 0; k < transport._convertFields._dateFields.length; k += 1) { - fieldName = transport._convertFields._dateFields[k]; - if (typeof (data[fieldName]) === "string") { - transport._convertStringToDate(data, fieldName); - } - } - - return data; - }, - _getModel: function () { - var transport = this, - i, - j, - fields = {}, - schema, - value, - type, - element; - - schema = transport.jsdo[transport.tableRef].getSchema(); - for (i = 0; i < schema.length; i += 1) { - // Skip internal fields - if (schema[i].name.length > 0 && schema[i].name.charAt(0) !== "_") { - type = schema[i].type; - if (type === "integer") { - type = "number"; - } else if (type === "string" && - schema[i].format && - schema[i].format.indexOf("date") !== -1) { - // Set type to "date" is type is DATE or DATETIME[-TZ] - type = "date"; - } - if (type === "array") { - for (j = 0; j < schema[i].maxItems; j += 1) { - value = transport.jsdo._getDefaultValue(schema[i]); - element = transport.jsdo._getArrayField(schema[i].name, j); - if (!transport.jsdo[transport.tableRef]._fields[element.name.toLowerCase()]) { - // Skip element if a field with the same name exists - - // Calculate type of array element - type = schema[i].items.type; - if (type === "integer") { - type = "number"; - } else if (type === "string" && schema[i].ablType && (schema[i].ablType.indexOf("DATE") !== -1)) { - type = "date"; - } - - fields[element.name] = {}; - fields[element.name].type = type; - if (value !== undefined) { - fields[element.name].defaultValue = value; - } - } - } - } else { - value = transport.jsdo._getDefaultValue(schema[i]); - fields[schema[i].name] = {}; - fields[schema[i].name].type = type; - if (value !== undefined) { - fields[schema[i].name].defaultValue = value; - } - } - } - } - return fields; - }, - _getInitialValues: function () { - var transport = this, - i, - j, - data = {}, - schema, - defaultValue; - schema = transport.jsdo[transport.tableRef].getSchema(); - for (i = 0; i < schema.length; i += 1) { - // Skip internal fields - if (schema[i].name.length > 0 && schema[i].name.charAt(0) !== "_") { - defaultValue = transport.jsdo._getDefaultValue(schema[i]); - if (schema[i].type === "array") { - data[schema[i].name] = []; - - for (j = 0; j < schema[i].maxItems; j += 1) { - data[schema[i].name][j] = defaultValue; - } - - } else { - data[schema[i].name] = defaultValue; - } - } - } - return data; - }, - _getData: function (options) { - var jsdo = this.jsdo, - data = {}, - params, - array, - filter; - - if (options && options.data) { - params = { - tableRef: this.tableRef, - filter: options.data.filter, - sort: options.data.sort, - skip: options.data.skip, - top: options.data.take - }; - - array = jsdo[this.tableRef].getData({filter: filter}); - data.total = array.length; - array = jsdo[this.tableRef].getData(params); - data.data = array; - } else { - array = jsdo[this.tableRef].getData(); - data.data = array; - data.total = array.length; - } - - return data; - }, - read: function (options) { - try { - var jsdo = this.jsdo, - filter, - data = {}, - transport = this, - callback, - property, - optionsMapping = { - filter: "filter", - take: "top", - skip: "skip", - sort: "sort" - }, - saveUseRelationships; - - if (!this._initFromServer) { - if (jsdo[this.tableRef]._parent) { - this._initFromServer = (jsdo[jsdo[this.tableRef]._parent]._data && (jsdo[jsdo[this.tableRef]._parent]._data.length > 0)) - || (jsdo[this.tableRef]._data instanceof Array && (jsdo[this.tableRef]._data.length > 0)); - } else { - this._initFromServer = (jsdo[this.tableRef]._data instanceof Array) && (jsdo[this.tableRef]._data.length > 0); - } - } - - data.data = []; - if (this.readLocal && this._initFromServer) { - saveUseRelationships = jsdo.useRelationships; - jsdo.useRelationships = false; - data = this._getData(options); - jsdo.useRelationships = saveUseRelationships; - options.success(data); - return; - } - - if (!this.readLocal) { - // readLocal is false or _initFromServer is false - - if (options.data) { - // Only create filter object if options.data contains viable properties - for (property in options.data) { - if (options.data.hasOwnProperty(property)) { - if (options.data[property] !== undefined - && optionsMapping[property]) { - if (filter === undefined) { - filter = {}; - } - filter[optionsMapping[property]] = options.data[property]; - } - } - } - if (filter) { - filter.tableRef = this.tableRef; - } - } - } - - callback = function onAfterFillJSDO(jsdo, success, request) { - var data = {}, saveUseRelationships, promise, total, exception; - - if (success) { - saveUseRelationships = jsdo.useRelationships; - jsdo.useRelationships = false; - - if (transport.readLocal) { - // Use options.data to filter data - data = transport._getData(options); - } else { - data.data = jsdo[transport.tableRef].getData(); - - total = jsdo.getProperty("server.count"); - if (total) { - data.total = total; - } - - } - jsdo.useRelationships = saveUseRelationships; - transport._initFromServer = true; - if (options.data && options.data.take) { - if (!transport.readLocal && - transport.countFnName !== undefined && - typeof (jsdo[transport.countFnName]) === "function") { - - if (options.data.skip === 0 && options.data.take > data.data.length) { - options.success(data); - return; - } - - // Reuse filter string from the request.objParam object from fill() call. - promise = jsdo.invoke( - transport.countFnName, - { filter: request.objParam.filter } - ); - /*jslint unparam: true*/ - promise.done(function (jsdo, success, request) { - var exception, total; - - try { - if (typeof (request.response) === "object" && - Object.keys(request.response).length === 1) { - total = request.response[Object.keys(request.response)]; - if (typeof (total) !== "number") { - // Use generic exception if data type is not a number. - total = undefined; - } - } - } catch (e) { - // This exception is ignored a generic exception is used later. - } - if (total !== undefined) { - if (total) { - data.total = total; - } - options.success(data); - } else { - exception = new Error("Unexpected response from '" - + transport.countFnName + "' operation."); - options.error(request.xhr, request.xhr.status, exception); - } - }); - promise.fail(function (jsdo, success, request) { - var exception; - exception = new Error("Error invoking '" - + transport.countFnName + "' operation."); - options.error(request.xhr, request.xhr.status, exception); - }); - /*jslint unparam: false*/ - } else { - options.success(data); - } - } else { - options.success(data); - } - } else { - exception = new Error("Error while reading records."); - options.error(request.xhr, request.xhr.status, exception); - } - }; - - jsdo.fill(filter).done(callback).fail(callback); - - } catch (e) { - options.error(null, null, e); - } - }, - _processChanges: function (options, request) { - var jsdo = this.jsdo, - transport = this, - array, - i, - jsrecord, - id, - record; - - if (options.batch) { - array = []; - if (options.data.created instanceof Array) { - for (i = 0; i < options.data.created.length; i += 1) { - jsrecord = jsdo[transport.tableRef].findById( - options.data.created[i]._id - ); - if (jsrecord) { - record = transport._convertDataTypes(jsrecord.data); - array.push(record); - } else if (jsdo.autoApplyChanges) { - options.error( - null, - null, - new Error("Created record was not found in memory.") - ); - return; - } - } - } - options.success(array, "create"); - - array = []; - if (options.data.updated instanceof Array) { - for (i = 0; i < options.data.updated.length; i += 1) { - jsrecord = jsdo[transport.tableRef].findById( - options.data.updated[i]._id - ); - if (jsrecord) { - record = transport._convertDataTypes(jsrecord.data); - array.push(record); - } else if (jsdo.autoApplyChanges) { - options.error( - null, - null, - new Error("Updated record not found in memory.") - ); - return; - } - } - } - options.success(array, "update"); - - array = []; - if (options.data.destroyed instanceof Array) { - for (i = 0; i < options.data.destroyed.length; i += 1) { - jsrecord = jsdo[transport.tableRef].findById( - options.data.destroyed[i]._id - ); - if (jsrecord && jsdo.autoApplyChanges) { - options.error( - null, - null, - new Error("Deleted record was found in memory.") - ); - return; - } - } - } - options.success(array, "destroy"); - } else { - if (jsdo._resource.idProperty) { - if (request - && request.batch - && request.batch.operations instanceof Array - && request.batch.operations.length === 1) { - id = request.batch.operations[0].jsrecord.data._id; - } - } else { - id = options.data._id; - } - jsrecord = jsdo[transport.tableRef].findById(id); - if (jsrecord) { - record = transport._convertDataTypes(jsrecord.data); - options.success(record); - } else { - options.success({}); - } - } - }, - _saveChanges: function (options) { - var transport = this, - callback = function onAfterSaveChanges(jsdo, success, request) { - var jsrecord, - xhr, - status, - exception; - - if (success) { - // _id is expected to be set for CUD operations - // Deleted records will not be found and data is expected to be undefined - transport._processChanges(options, request); - } else { - if (request.batch - && request.batch.operations instanceof Array - && request.batch.operations.length === 1) { - xhr = request.batch.operations[0].xhr; - status = request.batch.operations[0].xhr.status; - } else if (request.jsrecords) { - xhr = request.xhr; - status = request.xhr.status; - } - exception = new Error("Error while saving changes."); - options.error(xhr, status, exception); - } - }; - - if (this.autoSave) { - this.jsdo.saveChanges(this.jsdo._hasSubmitOperation).done(callback).fail(callback); - } else { - this._processChanges(options); - } - }, - create: function (options) { - var jsdo = this.jsdo, - jsrecord, - saveUseRelationships = jsdo.useRelationships; - - options.batch = options.data.models instanceof Array; - try { - jsdo.useRelationships = false; - if (options.batch) { - options.error( - null, - null, - new Error("A newer version of Kendo UI is expected for batching support.") - ); - } else { - jsrecord = jsdo[this.tableRef].add(options.data); - options.data._id = jsrecord.data._id; - this._saveChanges(options); - } - } catch (e) { - // Undo changes on thrown exception - if (jsdo.autoApplyChanges) { - jsdo[this.tableRef].rejectChanges(); - } - options.error(null, null, e); - } finally { - jsdo.useRelationships = saveUseRelationships; - } - }, - update: function (options) { - var jsdo = this.jsdo, - jsrecord, - saveUseRelationships = jsdo.useRelationships; - - options.batch = options.data.models instanceof Array; - try { - jsdo.useRelationships = false; - if (options.batch) { - options.error( - null, - null, - new Error("A newer version of Kendo UI is expected for batching support.") - ); - } else { - jsrecord = jsdo[this.tableRef].findById(options.data._id); - jsrecord.assign(options.data); - this._saveChanges(options); - } - } catch (e) { - // Undo changes on thrown exception - if (jsdo.autoApplyChanges) { - jsdo[this.tableRef].rejectChanges(); - } - options.error(null, null, e); - } finally { - jsdo.useRelationships = saveUseRelationships; - } - }, - destroy: function (options) { - var jsdo = this.jsdo, - jsrecord, - saveUseRelationships = jsdo.useRelationships; - - options.batch = options.data.models instanceof Array; - try { - jsdo.useRelationships = false; - if (options.data.models instanceof Array) { - options.error( - null, - null, - new Error("A newer version of Kendo UI is expected for batching support.") - ); - } else { - jsrecord = jsdo[this.tableRef].findById(options.data._id); - jsrecord.remove(); - this._saveChanges(options); - } - } catch (e) { - // Undo changes on thrown exception - if (jsdo.autoApplyChanges) { - jsdo[this.tableRef].rejectChanges(); - } - options.error(null, null, e); - } finally { - jsdo.useRelationships = saveUseRelationships; - } - }, - submit: function (options) { - var jsdo = this.jsdo, - i, - jsrecord, - saveUseRelationships = jsdo.useRelationships; - - options.batch = true; - try { - jsdo.useRelationships = false; - - if (options.data.created instanceof Array) { - for (i = 0; i < options.data.created.length; i += 1) { - jsrecord = jsdo[this.tableRef].add(options.data.created[i]); - options.data.created[i]._id = jsrecord.data._id; - } - } - - if (options.data.updated instanceof Array) { - for (i = 0; i < options.data.updated.length; i += 1) { - jsrecord = jsdo[this.tableRef].findById(options.data.updated[i]._id); - if (jsrecord) { - jsrecord.assign(options.data.updated[i]); - } else { - options.error(null, null, new Error("Record not found in memory.")); - } - } - } - - if (options.data.destroyed instanceof Array) { - for (i = 0; i < options.data.destroyed.length; i += 1) { - jsrecord = jsdo[this.tableRef].findById(options.data.destroyed[i]._id); - if (jsrecord) { - jsrecord.remove(); - } else { - options.error(null, null, new Error("Record not found in memory.")); - } - } - } - - this._saveChanges(options); - } catch (e) { - // Undo changes on thrown exception - if (jsdo.autoApplyChanges) { - jsdo[this.tableRef].rejectChanges(); - } - options.error(null, null, e); - } finally { - jsdo.useRelationships = saveUseRelationships; - } - } - }); - - // This defines the JSDO DataSource by specifying the schema, transport and reader for it. - // The "id" property is set to "_id" to enable CUD operations. - jQuery.extend(true, kendo.data, { - schemas: { - jsdo: { - type: "jsdo", - model: { - id: "_id" - } - } - }, - transports: { - jsdo: JSDOTransport - }, - readers: { - jsdo: JSDODataReader - } - }); -}()); -/*jslint nomen: false*/ From 1e1a5a74f43f64ac834de63a45c6babc0f476588 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Mon, 18 Sep 2023 14:34:59 +0530 Subject: [PATCH 100/135] gradle build in container --- .github/workflows/development.yml | 8 +++-- devpas/build-image/.gitignore | 1 + devpas/build-image/Dockerfile | 45 +++++++++++++++++++++++++++ devpas/build-image/build.sh | 5 +++ devpas/build-image/scripts/initcmd.sh | 9 ++++++ 5 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 devpas/build-image/.gitignore create mode 100644 devpas/build-image/Dockerfile create mode 100644 devpas/build-image/build.sh create mode 100644 devpas/build-image/scripts/initcmd.sh diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 9941bdd..1fe343d 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -11,8 +11,12 @@ jobs: working-directory: ${{ github.workspace }}/Sports steps: - uses: actions/checkout@v3 - - name: Running Gradle build - run: sh gradlew clean build + - name: Pull Dev PASOE Docker Image + run: | + docker login ec2-54-80-142-101.compute-1.amazonaws.com:9443 -u admin -p ${{ secrets.DOCKER_PWD }} + docker pull ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-dev-pasoe:12.8.0 + - name: Running Gradle build in a Container + run: docker run --rm -it -v ./:/psc/wrk/Sports/ -e APP_LOCATION=/psc/wrk/Sports ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-dev-pasoe:12.8.0 - name: Setup Python - needed by publish step uses: actions/setup-python@v4 with: diff --git a/devpas/build-image/.gitignore b/devpas/build-image/.gitignore new file mode 100644 index 0000000..86789cb --- /dev/null +++ b/devpas/build-image/.gitignore @@ -0,0 +1 @@ +installer \ No newline at end of file diff --git a/devpas/build-image/Dockerfile b/devpas/build-image/Dockerfile new file mode 100644 index 0000000..f7eb72d --- /dev/null +++ b/devpas/build-image/Dockerfile @@ -0,0 +1,45 @@ +ARG JDK_IMAGE=eclipse-temurin:17.0.3_7-jdk-centos7 + +FROM ${JDK_IMAGE} as builder-jdk + +FROM centos:7 as builder + +COPY installer installer + +COPY --from=builder-jdk /opt/java/openjdk /usr/java +COPY response.ini . + +RUN installer/proinst -b response.ini -l response.log + +RUN cat response.log +RUN echo "Docker Container Image for Dev PAS for OpenEdge 12.8.0 as of `date`" > /psc/dlc/image-version + +FROM centos:7 + +# Add a non root user +RUN groupadd pscadmin &&\ + useradd -g pscadmin pscadmin + +# Copy dlc, java and wrk +COPY --from=builder --chown=pscadmin:pscadmin /usr/java /usr/java +COPY --from=builder --chown=pscadmin:pscadmin /psc/dlc /psc/dlc +COPY --from=builder --chown=pscadmin:pscadmin /psc/wrk /psc/wrk + +# Copy init/run scripts +COPY scripts/* /psc/wrk/ + +# Restrict permissions +# Read and execute only +RUN chmod 0555 /psc/wrk/initcmd.sh + +# From now on run as abldojo user +USER pscadmin + +ENV JAVA_HOME=/usr/java \ + PATH=/psc/dlc:/psc/dlc/bin:/psc/dlc/ant/bin:$PATH \ + WRKDIR=/psc/wrk/ \ + DLC=/psc/dlc/ + +WORKDIR /psc/wrk/ + +CMD [ "/psc/wrk/initcmd.sh" ] \ No newline at end of file diff --git a/devpas/build-image/build.sh b/devpas/build-image/build.sh new file mode 100644 index 0000000..542cd3a --- /dev/null +++ b/devpas/build-image/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "Building Docker Image" +docker build -t openedge-dev-pasoe:12.8.0 . --no-cache +echo "Building Docker Image Complete!" \ No newline at end of file diff --git a/devpas/build-image/scripts/initcmd.sh b/devpas/build-image/scripts/initcmd.sh new file mode 100644 index 0000000..95d5929 --- /dev/null +++ b/devpas/build-image/scripts/initcmd.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cat /psc/dlc/image-version +echo "APP_LOCATION: ${APP_LOCATION}" + +echo "Building app at ${APP_LOCATION}!" +cd ${APP_LOCATION} +./gradlew clean build +echo "Building app at ${APP_LOCATION} completed!" From a006ea066fe93ff231080727e3028959e769e3bf Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Mon, 18 Sep 2023 14:56:09 +0530 Subject: [PATCH 101/135] debug --- .gitignore | 6 +++++ Sports/build.config | 2 +- Sports/build.gradle | 33 ++++++++------------------- Sports/gradlew | 10 ++++---- devpas/build-image/scripts/initcmd.sh | 2 +- 5 files changed, 22 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 0dbf1d8..c5a760c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,9 @@ gradle-app.setting # Ignore Gradle build output directory build + +tmp + +goss +dgoss +unitTestReport.xml \ No newline at end of file diff --git a/Sports/build.config b/Sports/build.config index ad42272..f734f74 100644 --- a/Sports/build.config +++ b/Sports/build.config @@ -39,7 +39,7 @@ // The directory that will contain saved .r files. If this field is blank, .r files are saved in the // same directory as the source files. // ====================================================================================================== -buildDir="${ROOT}/build" +buildDir="" // IDE specific configurations oeide { diff --git a/Sports/build.gradle b/Sports/build.gradle index 1e69251..e2a62a7 100644 --- a/Sports/build.gradle +++ b/Sports/build.gradle @@ -4,6 +4,7 @@ * This is a general purpose Gradle build. * Learn more about Gradle by exploring our samples at https://docs.gradle.org/7.3.3/samples */ + plugins { id "progress.openedge.abl" version "2.2.1" } @@ -20,11 +21,9 @@ group = 'com.progress.openedge' version = "1.0.0" description = 'Sports App' -print "Name ${name}" - abl { if (STAGE_ENVIRONMENT == "dev") { - dbConnection { + dbConnection { dbName="${buildDir}/db/sports2020/sports" connectionParameters = "-1" } @@ -37,9 +36,9 @@ abl { // ABL App tasks task createSports2020(type: CreateDB){ - dbName = 'sports' - sourceDb = "${dlcHome}/sports2020" - outputDir = "${buildDir}/db/sports2020" + dbName = 'sports' + sourceDb = "${dlcHome}/sports2020" + outputDir = "${buildDir}/db/sports2020" } if (STAGE_ENVIRONMENT == "dev") { compileAbl.dependsOn "createSports2020" @@ -67,7 +66,8 @@ task testABLApp(type: ABLUnit){ if (STAGE_ENVIRONMENT == "dev") { testABLApp.dependsOn "createSports2020" } -check.dependsOn "testABLApp" +check.dependsOn "testABLApp", "compileAbl" + task packageWebApp(type: OEWar){ webAppName = "Sports" @@ -93,6 +93,7 @@ task packageWebApp(type: OEWar){ } } packageWebApp.dependsOn "compileAbl-root-PASOEContent-WEB-INF-openedge" +assemble.dependsOn "packageWebApp" task packageABLApp(type: Oear) { ablAppName = "Sports" @@ -124,20 +125,4 @@ task packageABLApp(type: Oear) { } packageABLApp.dependsOn "compileAbl-root-AppServer" packageABLApp.dependsOn "packageWebApp" -assemble.dependsOn "packageABLApp" - -// task dockerBuild(type:Exec) { -// workingDir "${buildDir}/docker" -// commandLine 'bash', '-c', "export IMAGE_NAME=${project.name.toLowerCase()} && sh build.sh" -// doFirst { -// copy { -// from "${project.distsDirectory.get()}/ablapps/" -// into "${buildDir}/docker/ablapps" -// } -// copy { -// from "docker" -// into "${buildDir}/docker" -// } -// } -// } -// dockerBuild.dependsOn build \ No newline at end of file +assemble.dependsOn "packageABLApp" \ No newline at end of file diff --git a/Sports/gradlew b/Sports/gradlew index c53aefa..2f856aa 100644 --- a/Sports/gradlew +++ b/Sports/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright 2015-2021 the original authors. +# Copyright � 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,10 +32,10 @@ # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; -# * expansions $var, ${var}, ${var:-default}, ${var+SET}, -# ${var#prefix}, ${var%suffix}, and $( cmd ); -# * compound commands having a testable exit status, especially case; -# * various built-in commands including command, set, and ulimit. +# * expansions �$var�, �${var}�, �${var:-default}�, �${var+SET}�, +# �${var#prefix}�, �${var%suffix}�, and �$( cmd )�; +# * compound commands having a testable exit status, especially �case�; +# * various built-in commands including �command�, �set�, and �ulimit�. # # Important for patching: # diff --git a/devpas/build-image/scripts/initcmd.sh b/devpas/build-image/scripts/initcmd.sh index 95d5929..1b278eb 100644 --- a/devpas/build-image/scripts/initcmd.sh +++ b/devpas/build-image/scripts/initcmd.sh @@ -5,5 +5,5 @@ echo "APP_LOCATION: ${APP_LOCATION}" echo "Building app at ${APP_LOCATION}!" cd ${APP_LOCATION} -./gradlew clean build +sh gradlew clean build echo "Building app at ${APP_LOCATION} completed!" From fa5b6a6cc72067bf1f0a44295fad22ea04573afb Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Mon, 18 Sep 2023 15:12:51 +0530 Subject: [PATCH 102/135] fix dev yml --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 1fe343d..8299d8f 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -16,7 +16,7 @@ jobs: docker login ec2-54-80-142-101.compute-1.amazonaws.com:9443 -u admin -p ${{ secrets.DOCKER_PWD }} docker pull ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-dev-pasoe:12.8.0 - name: Running Gradle build in a Container - run: docker run --rm -it -v ./:/psc/wrk/Sports/ -e APP_LOCATION=/psc/wrk/Sports ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-dev-pasoe:12.8.0 + run: docker run --rm -v ./:/psc/wrk/Sports/ -e APP_LOCATION=/psc/wrk/Sports ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-dev-pasoe:12.8.0 - name: Setup Python - needed by publish step uses: actions/setup-python@v4 with: From 9d1349a115ea743881abe3e17783e3ea351c4031 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 00:28:50 +0530 Subject: [PATCH 103/135] Test for app --- .github/workflows/development.yml | 3 +- test-app-image/package.json | 21 ++++ test-app-image/test-app-image.sh | 11 ++ test-app-image/test/testCustomer.js | 176 ++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 test-app-image/package.json create mode 100644 test-app-image/test-app-image.sh create mode 100644 test-app-image/test/testCustomer.js diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 8299d8f..a75bbdf 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -107,7 +107,8 @@ jobs: - name: Deploy new version of Sample App run: sudo sh deploy.sh - name: Test the app - run: echo "TODO" + run: sh test-app-image.sh + working-directory: ${{ github.workspace }}/test-app-image - name: Undeploy the app and clean up resources run: echo "TODO" securityscans: diff --git a/test-app-image/package.json b/test-app-image/package.json new file mode 100644 index 0000000..a852faa --- /dev/null +++ b/test-app-image/package.json @@ -0,0 +1,21 @@ +{ + "name": "pasoe-basic", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "mocha --recursive", + "test:rest": "mocha --recursive test/test*.js --reporter mocha-teamcity-reporter" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "chai": "^4.1.2", + "mocha": "^5.2.0", + "mocha-teamcity-reporter": "^2.4.0" + }, + "dependencies": { + "@progress/jsdo-node": "^5.0.0-2018-05-11-00002" + } +} diff --git a/test-app-image/test-app-image.sh b/test-app-image/test-app-image.sh new file mode 100644 index 0000000..6760367 --- /dev/null +++ b/test-app-image/test-app-image.sh @@ -0,0 +1,11 @@ +echo "##teamcity[testStarted name='Test_MasterSample_DataObject']" + +# Run Test using NodeJS +# export PATH=$PATH:/tools/linuxx86_64/nodejs/node-v6.9.1/bin +npm install + +node --version +npm --version +npm run test:rest + +echo "##teamcity[testFinished name='Test_MasterSample_DataObject']" \ No newline at end of file diff --git a/test-app-image/test/testCustomer.js b/test-app-image/test/testCustomer.js new file mode 100644 index 0000000..87dad75 --- /dev/null +++ b/test-app-image/test/testCustomer.js @@ -0,0 +1,176 @@ +process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; + +const chai = require("chai"); +const assert = chai.assert; +const expect = chai.expect; +const progress = require("@progress/jsdo-core").progress; + + +chai.should(); + +var jsdo; +var result, + newCustNum, + balance; + +describe("Test Customer", () => { + const options = { + serviceURI: "https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports", + catalogURI: "https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports/static/SportsService.json", + resourceName: "Customer", + authenticationModel: "anonymous" + }; + + before(function (done) { + progress.data.getSession(options).then((object) => { + jsdo = new progress.data.JSDO({ + name: options.resourceName + }); + }, (error) => { + throw new error("Error creating session"); + }).then(() => done(), () => done()); + }); + describe("Create Customer", () => { + before(function (done) { + jsdo.add({ + Name: "Test Customer", + SalesRep: "BBB", + Balance: 10000 + }); + jsdo.saveChanges(true).then((object) => { + result = object; + // console.log("DEBUG: SUCCESS: Result: ", result.request.jsrecords[0].data); + newCustNum = result.request.jsrecords[0].data.CustNum; + }, (object) => { + result = object; + // console.log("DEBUG: ERROR: Result: ", result); + }).then(() => done(), () => done()); + }); + + it("should return an object", () => { + result.should.be.an("object"); + }); + it("should return success", () => { + result.success.should.equals(true); + }); + it("should return a CustNum greater than zero", () => { + newCustNum.should.be.at.least(1); + }); + it("new record should be returned by read() operation", (done) => { + jsdo.read({ + filter: { + field: "CustNum", + operator: "eq", + value: newCustNum + } + }).then(() => { + // console.log("DEBUG: SUCCESS: "); + if (jsdo.getData().length === 1) { + done(); + } else { + done(new Error("Record not found")); + } + }, () => { + // console.log("DEBUG: ERROR: "); + done(new Error("Error reading records")); + }); + }); + }); + describe("Read Customer", () => { + before(function (done) { + jsdo.read().then((object) => { + result = object + }, (object) => { + result = object + }).then(() => done(), () => done()); + }); + + it("should return an object", () => { + result.should.be.an("object"); + }); + it("should return a number of records greater than zero", () => { + jsdo.getData().length.should.be.at.least(1); + }); + }); + describe("Update Customer", () => { + before(function (done) { + var jsrecord = jsdo.find((record) => { + return record.data.CustNum === newCustNum; + }); + jsrecord.assign({ Balance: 12345 }); + jsdo.saveChanges(true).then((object) => { + result = object; + balance = result.request.jsrecords[0].data.Balance; + }, (object) => { + result = object; + }).then(() => done(), () => done()); + }); + it("should return an object", () => { + result.should.be.an("object"); + }); + it("should return success", () => { + result.success.should.equals(true); + }); + it("should return a Balance equals to 12345", () => { + balance.should.equals(12345); + }); + it("Updated record is returned by read() operation", (done) => { + jsdo.read({ + filter: { + field: "CustNum", + operator: "eq", + value: newCustNum + } + }).then(() => { + // console.log("DEBUG: SUCCESS: "); + if (jsdo.getData().length === 1 + && jsdo.getData()[0].Balance === 12345) { + done(); + } else { + done(new Error("Matching record was not found")); + } + }, () => { + // console.log("DEBUG: ERROR: "); + done(new Error("Error reading records")); + }); + }); + }); + describe("Delete Customer", () => { + before(function (done) { + var jsrecord = jsdo.find((record) => { + return record.data.CustNum === newCustNum; + }); + jsrecord.remove(); + jsdo.saveChanges(true).then((object) => { + result = object; + }, (object) => { + result = object; + }).then(() => done(), () => done()); + }); + it("should return an object", () => { + result.should.be.an("object"); + }); + it("should return success", () => { + result.success.should.equals(true); + }); + it("deleted record should not be returned by read() operation", (done) => { + jsdo.read({ + filter: { + field: "CustNum", + operator: "eq", + value: newCustNum + } + }).then(() => { + // console.log("DEBUG: SUCCESS: "); + if (jsdo.getData().length === 1) { + done(new Error("Record was found")); + } else { + done(); + } + }, () => { + // console.log("DEBUG: ERROR: "); + done(new Error("Error reading records")); + }); + }); + }); +}); From 1c95eabe2439c0413bfa55009beca967d388ce15 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 00:35:17 +0530 Subject: [PATCH 104/135] Use correct node --- test-app-image/test-app-image.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test-app-image/test-app-image.sh b/test-app-image/test-app-image.sh index 6760367..5a7ff36 100644 --- a/test-app-image/test-app-image.sh +++ b/test-app-image/test-app-image.sh @@ -2,6 +2,7 @@ echo "##teamcity[testStarted name='Test_MasterSample_DataObject']" # Run Test using NodeJS # export PATH=$PATH:/tools/linuxx86_64/nodejs/node-v6.9.1/bin +nvm use 6.9.1 npm install node --version From 2f371ea8602af70a55cd4e987a3ce98c7d912af3 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 00:58:12 +0530 Subject: [PATCH 105/135] Fixing test for Sample app --- .github/workflows/development.yml | 5 +++++ test-app-image/package.json | 4 ++-- test-app-image/test-app-image.sh | 7 +++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index a75bbdf..6eab469 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -109,6 +109,11 @@ jobs: - name: Test the app run: sh test-app-image.sh working-directory: ${{ github.workspace }}/test-app-image + - name: Publish Sample Sports App Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + with: + files: ${{ github.workspace }}/test-app-image/*.xml + check_name: Test - Results for Sports App Services - name: Undeploy the app and clean up resources run: echo "TODO" securityscans: diff --git a/test-app-image/package.json b/test-app-image/package.json index a852faa..0217ad8 100644 --- a/test-app-image/package.json +++ b/test-app-image/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "test": "mocha --recursive", - "test:rest": "mocha --recursive test/test*.js --reporter mocha-teamcity-reporter" + "test:rest": "mocha --recursive test/test*.js --reporter mocha-junit-reporter" }, "keywords": [], "author": "", @@ -13,7 +13,7 @@ "devDependencies": { "chai": "^4.1.2", "mocha": "^5.2.0", - "mocha-teamcity-reporter": "^2.4.0" + "mocha-junit-reporter": "^1.23.3" }, "dependencies": { "@progress/jsdo-node": "^5.0.0-2018-05-11-00002" diff --git a/test-app-image/test-app-image.sh b/test-app-image/test-app-image.sh index 5a7ff36..e417997 100644 --- a/test-app-image/test-app-image.sh +++ b/test-app-image/test-app-image.sh @@ -1,12 +1,11 @@ -echo "##teamcity[testStarted name='Test_MasterSample_DataObject']" +echo "Starting test of Services of the Sample Sports App!" # Run Test using NodeJS -# export PATH=$PATH:/tools/linuxx86_64/nodejs/node-v6.9.1/bin -nvm use 6.9.1 +export PATH=$PATH:/home/ubuntu/.nvm/versions/node/v6.9.1/bin/ npm install node --version npm --version npm run test:rest -echo "##teamcity[testFinished name='Test_MasterSample_DataObject']" \ No newline at end of file +echo "Starting test of Services of the Sample Sports App Complete!" \ No newline at end of file From 824c00438fab6b44338f41ea6520179ef4cad41e Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 01:01:31 +0530 Subject: [PATCH 106/135] Fixing test for Sample app --- .github/workflows/development.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 6eab469..ad6bd1e 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -109,6 +109,10 @@ jobs: - name: Test the app run: sh test-app-image.sh working-directory: ${{ github.workspace }}/test-app-image + - name: Setup Python - needed by publish step + uses: actions/setup-python@v4 + with: + python-version: 3.8 - name: Publish Sample Sports App Test Results uses: EnricoMi/publish-unit-test-result-action/composite@v2 with: From ac41a655cc20f07a5b0eb9ea230766280208a677 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 01:13:55 +0530 Subject: [PATCH 107/135] Fixing test for Sample app --- test-app-image/test-app-image.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test-app-image/test-app-image.sh b/test-app-image/test-app-image.sh index e417997..c08e0c2 100644 --- a/test-app-image/test-app-image.sh +++ b/test-app-image/test-app-image.sh @@ -6,6 +6,9 @@ npm install node --version npm --version + +echo "Waiting for the application to come up." +sleep 45s npm run test:rest echo "Starting test of Services of the Sample Sports App Complete!" \ No newline at end of file From bc6813cbc25bea2aaebded30ad9cb173fa1b29bc Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 01:54:45 +0530 Subject: [PATCH 108/135] delete unwanted folders --- .../Sports/build-output/AppServer/Customer.r | Bin 47261 -> 0 bytes .../build-output/AppServer/GetCustOrders.r | Bin 14664 -> 0 bytes .../Sports/build-output/AppServer/Item.r | Bin 24343 -> 0 bytes .../Sports/build-output/AppServer/Order.r | Bin 25383 -> 0 bytes .../Sports/build-output/AppServer/Salesrep.r | Bin 17362 -> 0 bytes .../Sports/build-output/AppServer/State.r | Bin 16295 -> 0 bytes .../build-output/AppServer/UpdateCustOrders.r | Bin 16797 -> 0 bytes .../AppServer/sports2020trgs/asordnum.r | Bin 2005 -> 0 bytes .../AppServer/sports2020trgs/asstate.r | Bin 3161 -> 0 bytes .../AppServer/sports2020trgs/crbin.r | Bin 2053 -> 0 bytes .../AppServer/sports2020trgs/crcust.r | Bin 2319 -> 0 bytes .../AppServer/sports2020trgs/cremp.r | Bin 2295 -> 0 bytes .../AppServer/sports2020trgs/crintr.r | Bin 2211 -> 0 bytes .../AppServer/sports2020trgs/crinv.r | Bin 2125 -> 0 bytes .../AppServer/sports2020trgs/critem.r | Bin 2247 -> 0 bytes .../AppServer/sports2020trgs/crlocdef.r | Bin 2223 -> 0 bytes .../AppServer/sports2020trgs/crord.r | Bin 2409 -> 0 bytes .../AppServer/sports2020trgs/crordl.r | Bin 1775 -> 0 bytes .../AppServer/sports2020trgs/crpo.r | Bin 2129 -> 0 bytes .../AppServer/sports2020trgs/crsuppl.r | Bin 2263 -> 0 bytes .../AppServer/sports2020trgs/crware.r | Bin 2177 -> 0 bytes .../AppServer/sports2020trgs/delcust.r | Bin 6849 -> 0 bytes .../AppServer/sports2020trgs/delinv.r | Bin 2300 -> 0 bytes .../AppServer/sports2020trgs/delitem.r | Bin 3105 -> 0 bytes .../AppServer/sports2020trgs/delord.r | Bin 3352 -> 0 bytes .../AppServer/sports2020trgs/delordl.r | Bin 2071 -> 0 bytes .../AppServer/sports2020trgs/delsup.r | Bin 5560 -> 0 bytes .../AppServer/sports2020trgs/ref_call.r | Bin 2109 -> 0 bytes .../AppServer/sports2020trgs/wrcust.r | Bin 9307 -> 0 bytes .../AppServer/sports2020trgs/writem.r | Bin 6313 -> 0 bytes .../AppServer/sports2020trgs/wrord.r | Bin 1903 -> 0 bytes .../AppServer/sports2020trgs/wrordl.r | Bin 2135 -> 0 bytes .../tests/AppServer/CustomerTest.r | Bin 12461 -> 0 bytes .../build-output/tests/AppServer/OrderTest.r | Bin 4936 -> 0 bytes .../tests/AppServer/SportsTestSuite.r | Bin 2624 -> 0 bytes SportsApp/Sports/velocity.log | 420 ------------------ SportsApp/deploy/license/progress.cfg | Bin 5408 -> 0 bytes 37 files changed, 420 deletions(-) delete mode 100644 SportsApp/Sports/build-output/AppServer/Customer.r delete mode 100644 SportsApp/Sports/build-output/AppServer/GetCustOrders.r delete mode 100644 SportsApp/Sports/build-output/AppServer/Item.r delete mode 100644 SportsApp/Sports/build-output/AppServer/Order.r delete mode 100644 SportsApp/Sports/build-output/AppServer/Salesrep.r delete mode 100644 SportsApp/Sports/build-output/AppServer/State.r delete mode 100644 SportsApp/Sports/build-output/AppServer/UpdateCustOrders.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/asordnum.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/asstate.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crbin.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crcust.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/cremp.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crintr.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crinv.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/critem.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crlocdef.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crord.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crordl.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crpo.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crsuppl.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/crware.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delcust.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delinv.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delitem.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delord.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delordl.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/delsup.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/ref_call.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/wrcust.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/writem.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/wrord.r delete mode 100644 SportsApp/Sports/build-output/AppServer/sports2020trgs/wrordl.r delete mode 100644 SportsApp/Sports/build-output/tests/AppServer/CustomerTest.r delete mode 100644 SportsApp/Sports/build-output/tests/AppServer/OrderTest.r delete mode 100644 SportsApp/Sports/build-output/tests/AppServer/SportsTestSuite.r delete mode 100644 SportsApp/Sports/velocity.log delete mode 100644 SportsApp/deploy/license/progress.cfg diff --git a/SportsApp/Sports/build-output/AppServer/Customer.r b/SportsApp/Sports/build-output/AppServer/Customer.r deleted file mode 100644 index 461e68ece6584479d953ddf41188d88703a9e31a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47261 zcmeHQ34Gi|y`Rm|rlm*P(l!)<E2{3RctSS-fO4qFSmo3}0F&1u(ZECG++}8A|OIwl&E*YS z!W*KoO;ypB*0$KD(rC0Q+`BW*g3 z@F(~iVov;oll%ciSl_a(xsB2n`Ol6twS`;#i~NE4i~Y^pwpNDgWBwC0VQE9dnugVp za8rY#PAcuR)uGjqrlxh_w#JqQ|6;FHrEpcaDclxza*{e{8>0<%qarM84UdknGF;bS zDz=Jjm#A#R`l@Qb$skr$l~RMtK`H7Siuiv&+Vi!Af>+80LCD92E3N6zbj-l~^Q6fknV@Y+uzr-P4S=Ut8 zTpyMg7y(j=xGd7PT@s$`qFT0Y4L7$bu_Xq&xvj1q76Z&u0}Cr^RU#ItLIwOAt4cph zvRlIqk+!uF*rUn^u}WAH2oS-lNK9Fw%)DFF@~w4|CbMvh+%m7OYgaPd0*4w~npM>- za*G>kiM7==m9;c*id*EWx~6cfG8|Q6+;Ug7!I%`%Ep>Icb*rk0MXJyN|N3y-36y=K zo3|e!#O}>PJQNp#!tFsJ1`idYV~Qf02XBSwKVOKPd?D6UZ73;OvUGtdLDBJVyHAMP z!#${^k$wx?ev*YibhjF_#E2o!k1Tc}9)Le+;7#rVq`5AK#y_|mfKUF)D1LIqz)6G^ zVuJiNQ#)0H#0%kPKcyF{-0-2w@~YIss~`2UFV1J_+*H45{`l8#`oOYjFaG_^g*SZW zj4xfb`1WV_9{Z;w@96r&cVF0D!1>knx(>?PrwY*npzsKMaug<4jK(DAAFuq(ziV@r zh=+cDuq5QdT$Gxt2-68Uou4-mjvj{m04O{HpB#nBRTL_}ce?U3{rW^j&N8v=%0nDN zNlZ>iNs=`xV|(uQ*oS6I?^i>)wkrxJbE)v%T%RV-&yM@AhNI3EV*PSy z-4+FR9+SZcmms+p9G?}_ML)u28>_0@6-^Q~lAZ>eA}+vJD&8 zS66;CRIy<_!t1L`%c`qN*OpgRmWRqVtXo&UzPhTcvV7H=>a}aut*KsFy0&zES$TDN z<+`d>YpNu3XafYTE!(iFe06C}IhGDl8k*(<=#Tq%=7cy&4}A@l@+xr|78_yiVb%RlH8=rB?DfRZ^aVj3gz82eSk`4{eo{D44W_e|Mt zI-vwS6@VH*V;1~T@WcT-0D6h4Wsk=11YZ}R8_)yTSJw-kK0rTUAPfFM@C*ZX6K~=$ zmN7d8;3)$50f8*|OTZHZQ~*L*@YjH+7SITYX2GAafEfrP4T_+>xa4>W>9}UYK2-)6 zET6x8Il)quqVX%3kT%b`JzoQ@o0HEuH}^Rj#$SXIt#DU%Ua}C*_Fp}H(hR;jdT^qzFCqO z8)q4}^RLqVa%W8hi?E+2U+J6)IG+;0<~E}z5`f*tnWGNjXWT&K&8I3hY- z$DMR%Pp&Wyoy7ZTv#v>{uXpMisp%!&k1f1TU|;Uw5q9WkM{m%`&AgGW<8nt2{GYM2 zJ@zdzMdV$$ckf=`%y-G{74K>hxoDu=xU){l*L;+ICVoN^HzkjtH1k(aO_ ztN{hd*$mhMXackWE&zN1@MXZ)0apWV0o(!j1>ixzqkzW&&jJ1n_$xrItMqCa2zF{+ zXl-?nrts=l(~_^`uv!Q%%60KqKyE5K6&s0B1;!5;-rJ0K3&kp+JT zcsc=HfbJ~#d%)8N=m*g8_rAIz@azVR01Do>uiV&Efu{)I2NY+)9{^7YAPA_)f1xr|UjUvWfFBUZg1-bj6@VH*f3era8vpNojoJ^|`yE|>|K8WA`uYF1ui3mg+}hgGy5Q`l zx-FZrxv=fuPS^cl^X8V;$d*X+g4UMvA`Qf;obF$#(p`R~H`dk~X_lI8{^aQ{f8E)* zF~L#q>huY=W6n99x`@cB{ zVE>no3>^$O7H|?^H6YRL-J6JWG1v$vq!078nwJx|kS?RF4T^qyj~?Z(g8tnJ*Li$` zH0-&vK<<5sV>2K}^t?`Caz6YO9YMG}S%PiN4dJsR&EW=>EILatU!BYmc(tG%ImL}r z=zX}#l_PQ?X_7cp93qNvm1-Ie4=3Xa)kLHfh{Sy?@r@wAUGU=oa)r>-Cqt=!bTucd zyS*3s;sz&*78EtX3!Z$5nKmHN=DYlFE=RKT%021R-*{1_mo5M0nSH13z3~etZCO12 z+tV+qoBy}Xw_g3gcb>fSo)0`T{joXvju%&&8!9vw3q911x{x3lFv2c}8*T$B?^ zRv&}6TzEk0qXYR)R-d}Q11NK6L7rH`xg|TU0O*sAE~He^V3gy<#je|Rq!9g$^aU9)kU`%t=t!Rr`eaw-jQLcIrFcD4UmQ$l zAbBN|1f>v{-Y#Dd@(Y5#xVoY_S@>C>VoTn?OnoGo^p&xSo(%drS)WofS54vNGxl!=Oxc+!|yz^kEw#yoOz+{R$mr3W!SlBS*xeev|f|B$&JOUvFK_*6h zcau;!Yq(KJ{x!#`3h288_-anm_i337o#@3I8PN#AbnlUQslT5FX1HGXeSj{GE3E#_ zM{D@eVn&CPNi@E2re!=AbVlG^6_J*tg2wN@k0S*T)Ik6n>|&vfMt%p4@BqTY$X%4_ z{ywjGD9;)X@{xBv9!!=aqiU%aes=Ry_ukrkUSs(9Q_C)CJZjxfe$ce%wJin1g?GbXqf=a;?5$o$VJ<;H@=NRc-u^|nQq=|MKt1-rx80yszms8_ai%LTI`@}$`>UR& z>~|`1N!CCvQ~P^w4EN@u*XUd!gabxAYJ|O9D%QlvmL!SE+P|mepR@g&Isb9AeILN; zcAT0;`(py=0(77rdy-5BbU%~n=!W1p)AcmH(4W};$Q+Mf)6o93Daj;% zEYzB$Pxs@PZZow%(+%cda!6wP)8wG~{|vN0tq;llW!IAH-pqZV@r}jL+`RsY{CTmn zAHV)m-@NO0wvYVey^BvN@;qcBIozLq!`MRz2aI^s2>Xw6d35Lk4!ocdhOgV7hd7M? zeIoG4f!Uv`^`Kn%sS4tJQKZ8tlaXB+975Wq2wi3+gm4Qd?H{jTd?DY`sfBCH(Cef) znu(_Nxni8PHp&Gi&QiRdxlZb1eP3accm{pD4Sh61xGqnOABDaV$iEha>2MiE5327V z{824>Wx7NGbQI$Is*4gqtRW}iFCAYa^L@?W8vx$~{1Kn7S0h;lyO@ve<G%ej?@G-Ce>eCh<1Zaw;S5#2Zy0=yn4k0UmyWM!Cf#e|6W?UMZt(O#Rx;P00-uc5 z#&t5{1}h=eexP=-JO0urqWJ?l4_Gi=3;b1vykX=$Q$3Y1-?t3DAUb{~z9{pNPVKr! z!Pm`v*J&p5F>hwlH^h9`8+V+uHaZ+=VUpU*0?OXU(CjX(uM(ZnG51c1e z4@goHOUd#AY^+fkfoAuPmYlMBNC(?Ht>l?_R zuLkQ9Q(pnEQ%0*V0QN-NTH1mH;|e(e;6)6ggQRVvV;x zG=Hs;2P)SuC5sTXyiVh&^r&3-GNK#ly-CVM$pb0y%$nA_k5hkbQH?XoMe_}uELZ3> zZ@Dyo3Fm?CC8Bcelq@J0)=##(8i@ND*@^T*w4v!Pfu>(Eg4SQAqx)>gj`|Jc2x#bS z0hG@?1D`Z_81B~s6d$Wn>He7_fOd2rO*;1=Cq8KrjW_Cx`vq#OR&=lR)|cjQG|GG; zWa(=O&Uy;C4F;an(cG`ElTkcbdb)Ei;@8(DJ5f%m=Z8oToV}Ke`&GYsSf=895$n8? z!#QGkp%i$FENS!+#y)PP7GR9i{mL9?x?XYjfjQvO{Jq?6jmXPV;2*W*h5SN%9=P8a z_zuvL46ljN^&%RTwDVyOpqkSzAG>GyZVE>DKufz1?dWPZ?JBhMAxyg=?dZui+U01M zFV})REdxo{Df0|Frt4r+@TZ**SJ9{9Pdgv-Ux+{L=t?8aTH5)Lw`us(&WG@Hqs%_! zZH9b)R8ja))`Rh;Zs%zJIV;WEEdXi85*gGeB zW160PdtG-8viBGh^x(b(a+=#-M^f6gQ27J}*zJ&c@Qx=`-c&AhWU7?R_(3Ll)*_vN z8rJsDwuLMNamL~uPc(n8UkMmOUTA!Mn&l0kEKw_e#~A5doCe|2+4V#F5YKvr4MEo@ zvw1vEdmN?agJ+nc-7zGWh31PtaDN1p1IFr3tSvjk{4spJEnlZ1uj>=WLSKLz!LwLhQ{5C?PudI0@^A;1WL zuB8_Pf-d){7=0K}eZ_zvgpk9%XY#2e^5OXI4r87mtaiE=>A2U&igB+OSd;z8(?L1584J03((3u;RtQ6fIiJoyCahhje zQcR+V+e&k53)kn`Ovav0^B8>%YF@i1+&Du3Wlp>)HpN?vgK0 zl9cXG0Y+l&YL3S^574gN2y2|_{|`qdy5p0RtnHLrT-xctg9pY=)pZc6BULd&Tn@@+ zXPPoyhI-Exl8;Qwaa4wZG<-D+)Vk$$=IDTZ7NcHkjk?7;O}ybqggnsD%AB+%sXm92 z_R)JdHVhsy598Tnbsm(dJ;@H!@VOXmD3@X2UxoSxWobVD&gs&nz7 z6<#~g{5uT$r1JUiBqRJy`It>jnQM2-E%Sb;TMgk;nrO!pCYFx?l|-95>kf63nT{b+|^ zwK$$f@E0=NkLG>Xrp~eQD;Bx&p@0y50CLfj@MEM&r1dj}TWxld)=M@Ik5sz-b^~PD zb+*6Pi`d4eb;Bqh-H$ev^H0y>79J`eZFczkkmQ_#Y zJm>TlUr_jK+bx1sZ~}ti&~IQ=@ML&ePC}waSmmHIHPWwS5|Jvv z4(xwYIWilqr`VF`@K|_#->BB3@3Tv#$L|PAKFS!$GPnc!+6|q$uBraXSkWTShM~iLWq${0$Q+m4n5T)z=kbj8~rO4bg&R{#+W}{)F$V`I6@B){E zKe7=jr7lAPse$l`5c-7a3ZTE=(50p5cGGeFY`E5=@o|YB_Z=W^dU5s8yReD{iTL?v*DNA5>b7Q`FB4>bSpu3reR0GjHDZ!BtbfeJV)AVfeXrPFbhh|>= z;Az+8)bBX*=ogZ60a%s(;Pd@z!(Gi$!f~et+?8gpd?>l=70~j-f|?Pv&%tt>fL9O)EvP}OkBgF`tymOfv$^^JT6C!@WyHq z)9nJC-$O%@T<1F7WTtx^bojSijyRGrQZ&r!w99K$lG3QOsM&bn{Zk zo5pn0n65m9yrY?JI@49Bkf*PG&S1Lc6!LVNAIx;mf{yx#*NwxqOyy=W-QSYx9mn;2 z(XLrc_hwSM@p6nrjqRMxbTiO@{a&SrjTdTYz8AOgC_A_abjj+Oj|A~FvZDKG3Oaqn zIEU$ekyLN4bDi{1ru$tAdGF^uzmMq>itsOn`1Pz)OCDW&_?&rnm;dTNUVL!t_G2fU zzI@%J-yf5|;xn^edEcymw78j`tIl*^d#3bE&q;3i&eo#E7o0rrswY1B+aH{AeA73c z|MPRtywh<}sJ-RQ;H5p2GVNpkuldKi{xa2Xrt3nP&f)*j`qTC|H-G3ud*8)HlEUlFTy_$iH?9($x8B#iu_fX7YDA^jvDhnUjc z{GkF43EXv&K7BU!843B5!?L5_*9mFvKIE%NpfjONe74>KQ9Oy(47y$>=G=%_iIZ>I zT~m4{xjw7B6D+yD1i9)-rA%^jb7-|p?Wk4+KHpKORkz&g`A4*iRUbb=eNbK5c7S)N zXJH35jveGfCOMK#PWnL#Q9cu0s{H2V&`EL#a@7oHw~G*_z2GeeR{aX`%v9H&Qn!!J zQEndDBk805KzTg`U*gdYcS~6H_bcR%?oG1sPXJH$VAt!UtooN*uR13XFP-#*6x_FZ zwevM4t9+5Ysc}PQ20uDq2NOa@{1i%Xw*JEZWBt<)wEmp;6Xx&|ZY{D))Mh zI)&QT{1nTI-T(QaS(hfZRQd5)^)s96N4+!QLgCxb^)mtap}BBh^F!}35Mux5$B+D= z$@V!v>OBJcKR-t!KV%9%%9QNS#S}=GC+2~UOxT|P=$=~Y54kzCqf9-o(~gtq?D=Og zfTKC(G3cxgl_aR>sYIdi&Ed$fGPJmNR+& zmuvkQ^6!+>nwNv%r};OfzAWW_7;=-h7ndVTxs{N6&=}LX>L{Df@Tgz7W;!+H0!% z_vO+!JtD`oUW78Yr@GTKrM!&eq-{sZGREzhushp6elP8k+Jsif|15Tp>HLzOFHN>? zN;R)(`fTMo=b{JboQCdN-k;|*@!8bA`~RFq*Xvws{2njzW&Ts0Lj;Rde#Yt?BGdje zwVkLZA2O{2bh*>7^W5>>rc13~*|j~s)BT~ee@$F3$7TQRz&+5^zKQEBbsjE*E3m-we>{&PM_^Z}z_3fAePZt^LkB$|F^%f<{?`vf4^p zTkE!qdR=)cx^i0#?}?Aa%A4Eh8-KdSVy*R#9QtVUhIQqY;_OHhJ~klE7v9&*m9EC; zmlsrS_{f6QYu2t6#xsKSdScQqD)G5zM~vsC#p;cvk_ex5rtd#%ZPvR?)vGNf;l|J^ zlhAtUrFsXVBwSU#7N3T8BwBAr{A8@9IaJpgLy;)B;jB-F>)S;AM;gPeVbOT{ws7lq z(X=rZ#%J_eVv)8;OS6bbs}OZ(HLX^45m{H)UfFV9Ohl+!WvG5bYeTqo<#wS2uERI` znplnWL|aQ#d??(urnxP=1-fHkG$bOWtg)`OuAZtIcHF|!K2lL$S?(9Yf7W)tm@nwp zPrs1{e?vG{&q4BF`3{Q0Vufrlw9UHm>WU4k1U`qoX4UF7kExqk_hxa82UD}M0z@IuGB<^V6OhM`q)Bk?`vDOMCuo=L2Z+zREE#l7RGpz zjsjnEZ;pk88SwR`VKaQ5W=tviHhPkq_Xb3H73Y$ z%|kw_OX~0pCcx3%?;$`XpbAh8*a$cS@DTvH9gyc-p;V}Ze9)5wgRD2oen0!nB0VZ6 z78k-At>l}A8<=nee3|rhf~G=Mniyg|B^+ll(jyeLur=t3X5d;R1#ARFne-KarkCZ@ z8_dadv#(<6mr&H6*4Qbw1NUjbFbD<#>K!ap6SOZ7azy2BC*y^b1W@n8qY}7u+!Z8U z05ru!E3aFSW>pzHEQ=7*Dx7q(QtlUOycX?9qo>~$KV3_Z5qlsPoj`XubYxAGCw%Ew zjqsumX+2IlT{~3yaEE?LE`~re=+IGK6rKLERsqD0IO%lFg!Sl&Lx~Xqp#3t5%0%y* zVLE4CVM2Hn4jr9}asB97$0ZX{&`~N$R88yl$95okk=_TNzU)A*n|&2izh&@<-#g{2 zH>X|!{8s=SfUg3+2KYMQO29V&-vo35t^#}uKx^LX0M`S$05fhx1@r>$1pFLu7vOHdJ%C>V`T+L=?gQ)u+z#z`t_xVbXjSk%b)4$!m%)N|LQ z(4{l`9lZs%3!t=8fGkHC3lkX?14!z3=qQJZPWMlu8g|mDUri(wttzA|1WN&cxQLs| zNilPtpt}-u`EzA@p3tikdIN4F(o3LYx735WAVHR!fjDVbYZW|6fbbiDJ%ACwn}D|f zZv*J*`U%J(t^@s6cY5O{xi0v<999_mogfO) zEIW{oP5_m`*pEoE63PtPB`EzoQNezQ@ipw%vLBUxzG&xooc#{=JK67&em=h?KTq^A zet`W!_J`Qt&Au`typ2nmlaE%$($O}L9IaF6gX;Q=>AcIM{zGfc#gYg=o=qR|Szjo( zw}0l=k7PL8KZ?M4c7cfNFMjJU;2GONN$>0lWen}jU*J}oLxuFCjP!MeLl8ALij+FS zGQjgyTW>L+sOAajBharJpaN($C|!)8k$%PVZ#D zi~Sz<``GVie?aCn-rD-Fy!aKYJ&bOWI2 zbLgB4}Gwwrmql4_afJTI(9OHGOK+mPp$sy?fjcjcuxmwzRgzHkC%BRpHii z!>ya@F^08l4Yw|gC^_^y03EHTQE1=g>9Q=WZ;Fuw?WQ6yk0bQu2Wlb0k>8U?6A3-- zujWs9($RSIokA6E!@TRqr190*l&*It|KY^ft_^g}dS_SXU1x4v`bge0cOKOB!Ug`@ z>(-6tNktZ))SI{aPp{r_asIg7uarm6+j-qJf4C-e!H(;WdSO@D^v{LnRZ8o(88`Eq zO5m9crob>3fve$Qf|K>GRZ%vM@W6=C6|VnFSW-t5>GMGLzb5L%O*E~hk)cgXUL=WyekWNec&Om<&i!7 zAq#oCz(ZimBYQf;u^G3#nVzUQ96SUhkLp#|AKBvvJn~dKWqJbVMDQ$^eBO2{2W}nX zshwa`v}F=x=M551F*o0to|KA!hrq6flYv=DXtV){12e04X z=bmw>WcSUhmwV{xEwIYbp3`;o_E+U-FX=jZi>h)DyGVrS>tf0cd+_v@R^@0f;W~Xv zhq+CeH7C(qTa`P`!po@=Nh>^fddsYGjUIfSe0qaYF7e>$t*^>;S$H0@1-&86b@WzQ z<@zkVKA(Hm!qa|=+#42N?| zh|}HqpVDaspw?xy4j6It6@y91&h#WFUNt4gOC$Eq1GJ9#m?N)2XH;Z*@|1E#POmjb zd40rKrkg8r?CRww%xQN|JA32%ir2usdpbr5gN=Muy0TYb_ z0(~^FVEOX-%a<=*xOAz53J7_TSTe|vdv2g`&MAUEKVWoo8!qJ0DTBJ0%-G~tmJPk? z)WN?tvNh5s7A?W6Pn>fHqvQ@Q;5~+rTI9imI**WXc`%_)C1jirD2($7g>J#u;hX$r zxN3<_TU}E)hRdI-QDn?dQ9iYh419p0&MzcJ1=V$KYBi&+jiA}=2KwI?pe*lS#sc&~ z2Oo$S=4+*_z}ZJjYh+8Lc>%5(M;bP7R%VxgCBkrPYfI|_=kP?@T>_Q}y{9Qm=S313 zG(uWkTNqkBSu_(?2@}q2&fhWB_y&6}-e*y1a*7Fei`|73F=dI7jA^bU3@Y232 zV*2#@yyg2VcwS|`J!7KJ+kSL?j5ln~8!Y!#ZTj+g>$3>@4&xQ+vFz_(ect+92puP| zo(~=XeIJI7GgwdknCR2HL{)3&K*wiT&lko-pSS(4gbw|MgX_mcpR;k11^ff%S+Jl7uG{>Sj)zx@9+9FO-X(S zI@BHA*sFTZM@FwXy*_V!PJxaYtmlvepsxx#YFN*iW1!EjrCR9t6zgd@0Q$B=$LCqk z&8T%cmBHtI=;_(emo}nyz|jL(6N*CJTfNwyyNwg&@se%=>EQJ zT>Ad!Eg!vg@HN)+=9uX7wqGuW?*bmer+J6+Q5Q9R`MmWx2Re>mJ?|eAect*EK*ti+ zbLs)mw+=dLSWkFN^m*4WG3fX#>$zl1^m*IwKcM4k)^o#{==07;H$%twSkDi~M4wkE zy;burM&0X8+E4w#TfRr3<9Dp*xiQh_UB3)N$6r~`o-xtqUBA2s9l1Q@Oz;jpV^W`m z&@q$s9C`rs(Y?waWId;4sn2+Dhi5U{~J6sSUv;@9V$0@8B)FDkO@V*mgE diff --git a/SportsApp/Sports/build-output/AppServer/GetCustOrders.r b/SportsApp/Sports/build-output/AppServer/GetCustOrders.r deleted file mode 100644 index 416cfed3ac2fd63931088a9c8da545bc4e9e144e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14664 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 diff --git a/SportsApp/Sports/build-output/AppServer/Item.r b/SportsApp/Sports/build-output/AppServer/Item.r deleted file mode 100644 index aeecf8c7942049897c2f237023b15fe59f3f6a43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24343 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 diff --git a/SportsApp/Sports/build-output/AppServer/State.r b/SportsApp/Sports/build-output/AppServer/State.r deleted file mode 100644 index 646ef7789aa4ef93b7c62952f5e8dcfaf23e7470..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16295 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 diff --git a/SportsApp/Sports/build-output/AppServer/UpdateCustOrders.r b/SportsApp/Sports/build-output/AppServer/UpdateCustOrders.r deleted file mode 100644 index 4502bac929754e43c1ab551707922e7310b11bf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16797 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 Z6fl$SZ>yiL7D>Xlc>4eS7qWf{{s2Vk=ivYV diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/asstate.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/asstate.r deleted file mode 100644 index f519bdd963b52ccda6751b522bcf644bfdaff50d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3161 zcmb^zOK2NcaGvZoa+5S|8j52=cy(zot)s5pCOAE$Ua!{ng0!n>SHS@lqTsb1YFk2* zjSu~(H(v^d_F(9xr;=M+C^XQEp_d#=350@A#n7fWr`+0Hf;#i|J!x0owFzYKzWKbF zH}mH6tbqr=U;kx;`GC+5KlGnSLmFU~5f9)7C}!P?k&$TlIDn>&O=-B`cWJG;aB%`9 z1Xnc3>z2)p56Y#6UvHG_m8G@ikIRdhjgO6vzR!)dMzgx&*Lk3p>G0a9#_>-?y7>aYF5YbDB>9jy+CnnbQv60lkt=Z$E)<=aqUk2L1&^GUbWB&yfOi?B&X4~3$-^(6 zp8J0K+|twB7F+po>*SYb*9U+5`metqe|7h=QV!e%1y`~dd}O@$pE2Iqei{=1l{cc} zAW6O%E|N6-`NE_5;dKD}Dhb$3^t_~#0%F zce9o&j0+APlrx;HIjzr`IMO-k1Un1EqKM^m37S$&txEVsiH}K)6Dj04cLL*d3XHFa zz$YX=iI~oKS>jVt{t9C9_p0PiOZ=L|0LS1s=4J@a!<+CnybG7%Don#Qn1heNhbq)j z6W|F3Og<`?g-?y7zwIu4TLyD{-lKP{XExK$N7q*R0;#vrsyN!~}5 zGg!evq(#Swg<7g;&RC93>QrY+Q*WKAN>rmLwA)cW%C!-q5`3mnz+lHYySN>`=Y zFv`DB(`Y~vfrMqs5C`ybYL@7o zKMMMKblyV))tC1|#?((LZ+lWF+)t#+*f6f${L(v0IZn!NlDsc<2CX*GA9W^XK6MJ$ zN#sWaf6z}_`q{?)uK(A`mOe($*+?h;K6X8#$7Lfh7PaT7^#*>7y@f09*yDc>X@Oz{ zZ_Wkv`>{)uTpe8c$qCl-vcFha=7Qs^l@*^aRO`IC$;mrF47f6 z3tl{U@FW$jc&s3J@KPv9Pf`%+%_^c)5D^brP*`WO`;y(wrd9C^^WJOXtt26NREG>r6}LCz!08s-j;sil&;-b%hz1nW35$Lyy!W6^#i~9W%}p zv~FFDc6J^V`wE)q_`6A!__`thdJC=x1xUvojgZgX*#T6K!XkQipq5hPSgiL@SCmGC zVGYCPn!`MP+oFfAHF?Me>eoNW?N8AJjnj*t17k(b%kK6aUNv)fupi9I{n@0_xC}&J zzF>Gg7eMqGJFYG~oo&1IF1HkYbba~G@{tGcDj(d`8g9{Ydd8U`u+JDWpYN%W4^m6d!YqZu|JW> zCI{r4lFa1Nsi9;VyW>MA`%)QHs7<7kl|WhsWu`Kto{RfB0AH>9GTLD~K8fAPIq-Wx z-vppHupVeT{G2LV3mM$V9x@bueVg6&a0=iimLE<{Tlh!V_IiOfBIqw^c{X0qSU%`L z9`~HX4Dz=q9P?ah(DTr+IqfOI@`MQj4xXO5^7^A2B%?DJ$5v|*VgBro_5I&JIDQMe z`gq&6-2A)-sBWlcYVfm~9l(N>Ctf~`WD7U&gK#;&0h=c9AQHKcY!J3V4C43`gbw8U zkvrH75rNj0VdWxz{xhWuj-96u+J{CIz;P8hrLapSrj0S!i9wH@z-lW%lSO2P8pHs~ zWJRlQ_-y1kjY2E(f@UOGC}mO*^zONx_-(SebzWN;c|50vy7O(1|EK@ZN_zMW207da diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crcust.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crcust.r deleted file mode 100644 index 4b0b00d9d400a6256251bd066d4d512a4173182b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2319 zcmb_eK}-`t6#ZMPfS@80C6bs%qcIBD5;4UCcG)h_q}{FEEtX^r8oD4+O5L`Ei&Bk= z965S~L=%sA(nJr$c)*JxAtu~J6O3Lp5e}OAXLqLEmaS;wZ)V^B@6Y`CGk<2cb*t|N zUruv6ptxbt-B1g)00SJc0Tc@yo^uWyq1I{uyk1|g7a!QVa@o;#KQctRy%6C;l1I3x z#WT8*(TwXia<^`3W8+y(PU|T#Ii}0$jHaX~jci8a(`iLFrgTHgm_(McZpnam|gZ+5@nJjp%7yVFEj}(3PG={SwTh*MnL} zk^TOz?sgyQj2>q{QQzAu>boNkR)?DxS{q&YHya-} z@2!7fJ*{c1iZjjucg%O?AM@S98j{kmeXCZcYW%E2k)EIWnmj%QAoY+HaQ)@$s20iU z)q?pv!kBlV37(=Z#1&PFMC+msDO*6+SE~ zF)=F0k%%a%iVzcnAvGL|gw$R>%u9l(im`|i3@PT|sEmQaf*ch4_=Jd8I!q_QlZ9!K zj4>4;TT66;yD8%RGHrZIn27Hh6YD7MrC3j~f#NBy*4hPUdN%c!(lE z4b&rh6pq0OI0NV40`x#1T*k|P6?B*c1AAIR<{9B)bZ0d2laSPVXWnavG6hY%&6{)Xx$oWgpK0am^}dxk zi2+(UEPJXepaLK)kq@BT;PPB>(MVd`2jKHHb@=dsr6rRdZSbQ)=J!KHR-@ivYoa&B zlBq=U`gmsYMq+FtozP4+t&EH@%}gb9b0(QiC1lgoS@I@JCQ_Er>}&R=lVhpGaFR`# zji$G$zW#z&nKCD5ZZqb!D>deQpEbe_4?v(C;3XE~CeExMBQ<=6XOUlmMT~lpsk-L( zw_I#!Lhfq6uvr$<>}G3@3Tt~D&MtKIFLG~9Q3hpO1&81xpjc{EX_dbqG!QMWwLA3 z;dmA?=RK%_C&+tb-H6H&rBjXDL=J^$!#FetRbxOmWJ8H*2t7e9q=b~1L5PZKs9H3v zMU+@riz-*Nc(+CRa&(3;9IN~j)E6X!WlRZ zZ4iVW=miF|a0{|n3xIM_zjUW}vLhH2`%c`=O6hKOH@`hT{%9WHE?y_>+T~=pwQ|#P zIX6{kIW)lqW~N8j=)^P|5rP8)j&)c<{evU6>fmlvG^NBao zFj(Ar$$2q|en}s~LSu7UzHctxN6d0gZ?@t%g_f{L Y{00<8N)g9;BDcR{f&cWc!AcK*0LF#~p8x;= diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crintr.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crintr.r deleted file mode 100644 index a5624c7fc51b14376c2a3ddd7a83ca2f32120bcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2211 zcmb_dO=uHQ5T1=u`y(hQSP)@V{KK}S#bR0tCT*8y#bjePscl`+(rjZQ%&g5aP`4zIJ!JX;qx?c7DE@dGp@9msk7l zP2Y^ZOAiq4A zn>}}1Jl`?@oH<+5SUqPpAHHF|Z~wC1684Zbr8nd#6({@6-~&AO#Ey#CB&qRETG&aCh&VEJs_rqI7+5ZPc$u% z|3FemN@$c)Jwl3sX)!L~6jUx%Ku4E28+emU8Sjh9@%Ed(_#XnarM0%b&gS*D9iSd| z!(QzAVFk#R)s;|71$<)6Cnr(7YMyoS@Y>V;GPs{2uX;gN z$BU{qtU?3EKcgfL;vi5abMrCGx*sb~8Ww{01-v2WbER diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crinv.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crinv.r deleted file mode 100644 index 48ac5e5df3b03377e036f981f82639b0c3774ab6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2125 zcmb_dO=uHQ5T1=u+gkjA9t5Q>qEhJ(NogS}#jeScteD+2yD1SCRGQZ|Xw#UVl0$2A zvq$kD9xGHRilQe4FFkrHf_U*_FKT=BpdOTU-tNA1)4r`1oG@=@zL|M%-kbNbt?ySy z-_!&GNNw;U*xmvy011KG0J-)^$X86QND6Xpf@sCEsTZe9m9i-ni#jRIkdj%pf&Nf`s8X6Jn`5PX zVWzJrhI@JjM5Qn@ogX8j6Vu1ckXv6Q2(Y{t>)HoE^&;*pOMUJRyHULefZk5j6kUr% z2G8||X#|gcV%qv1o~Idm>oG(B5YNK9u*BU_00O=V2M#s60wS|(?)DwK0WuT)d*{{z!pSRTR51g8toz1LV80wfCiBFAeuwZ8y zr`HVQ$iLS6ZkP3Dv4^xZ?A-j?OAtpd9lB~JwCQH4{#T+EiTRJ056M&Sr!jvn1#f2CaZ-U8P69;jsYGy>^X-; z#MeB&6_3tSSt@T$YhI{(as&Z`U&EabU)&@Oom=zB`ep{tpZ>A4|NCdhUq`V$-?HlN z{Ivg+*?mIQCI7b6;nPbNMdc{+K?bmcaBbEBi!R`yVDJ%IDL4!f7{;FYoJt5XhcWOZ;qF%5Z5C%3DlFh<}g1}!^*jZuI=of?aD zKuRD_+S>Mktx7%Dk!fXKVk_4Xtho;Qzrk<{-9r}K`@UJ36WlmGG@Wn5@PGOiJM;em D=nUwy diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/critem.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/critem.r deleted file mode 100644 index 032fa921e13e261de13511461994ac4555eb63db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2247 zcmb_dOK1~e5T1>(58K)zB1#dqf}+^gq*#m!nl)LIg(Mqy)7s)fE6p}7wK3))IVjCZ zd+<;^^&nV5q2B7rgCZV1Nks(lP>Z10s|WQU#+iMjf6`5>;4Jgc%r}q!G5_v1zkNOU ztjOtr)B;QH)+T5I2yrYUXM-pVNd)M%A;|^{kdNfId&3C!bB_w6W}D zUZ3vG@ZRq3OV1U6w7Qyub6b>OS&)uwN>IpC|nduywY{ijzxb$0EG?!S?`bhqtRs`hkQfusUtJkJ__^d;do*r6DuG|NBVH_(Ls@_MvN@i3Cu4f)= z=Aj*CQI`^8G&T@5gb)=2LLx4N#JC~_NDqo?K#}4JNsh^}un-Fhk%)}A7*s@A35p8h zBnS%0%COf~`qa1>kc7xZQ3{05GhaS1?2N;>g&i}AfOaQrG5 zIDT6U*=)%jmTW;u`K^|0v*b>cRI44C>UJRSLI!9=Y!7t85jX}ue1*@0j4$>G=rCpY z0cZ}IkKWnMh~}{SO1cx(!<}M(eS6gTa0cKu_7$gg=)6;w6IL!ep*B`VvUOV)(n)=M zGOZ_VfRTRY92O8?WqdCg-AT1mo#rgha3v;(<1lz}_2#=TR+5a)+C2U1dWMr<`LVhG z`)AW%La{NwTrOGlssF@ucd8pvZK1nDCHPqR-Viv5TemW-dQ3NdT8osx z5C^UjnF}-^g^?!=ZR5ahXB6GECYFpj5hx)SEeCz6Y@DDuWX`(j>nk&ZE9Zu$`8V?a LufM-Jfi?aClaTjZ diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crlocdef.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crlocdef.r deleted file mode 100644 index 575ec930993491379d70e49e3ca3808b38c961d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2223 zcmb_dO=uHQ5T1>(KT=TfQV=u6l-X zA7@#9tjk#_E_QXbjPJLlvQ}^jMvpQ3uz~zX#`m1Te$Wl|o6}n5>P!sB;ow^R*1IoG zkc7_WIBmNT;pdls?C<~nnfmLf_QqRQ-N{e-Pabks$eC$~*J|(pb@=d*wP(;E=hPN= zBOKH1fJG0Z!{U6wyNFY845BcGKaM+#b`RQAjzW+lbtSc$>3(vO)n)sPlM&5)I;(&? zj(o1X#+;(vsA}cB2HhC@gd|{ugFu~_n+`XPT6b`>VwX3MPEsQ)o*K|`2cNn#fV_E{ e{lv9_59i#3My15V>?>mWl^OegUwL~5`u7Ld%H7qTehIam(0BPy}x-gZ>HPE*Dpq& z&PygBwZNLcxd9pgh9$xP%wZn$qKA%D{TLkx^b7`Y!qHbOOmziOLlg``EUd=mNPjAs zH?4fix|}J_UP(>Q6jEBw%qr<=Q_JO3EH`Hr@~Lnx$4u+GX{GYEqBqbRC|J|^)TA|M zrMq)-PiN;DS+mlnB|9G74hXnvTlWJj9>BT|0+9R?ceYG+zQYa_2S7&aag9x7EU2%M-db zq${lcb&#v_)rRlH63A7`jUx|>7t34guV#(~9;c5cZcN?GN8b-@_}*=1L+9^edmM6> zak?#Z9(>Pw=^FeEy~Q3o2l_?}f!n&?1x*BJOo{Si(7H0{~Q1lpM z;Sq)DN@7qQ88I|9O2NeKY|IE7Np>NuE5llnDYTZZL{$URMe$}~SyVBWVsny7x1V!- zfi|W_($V9ugv}c{Z{qwA=gr6|M+@hNxxSU-HjXJ%E41Tn>VRH22O&@(21yu)3>46Z zh;q|<^mcb6$|?FwvKQ6Ud-?wQ_Ta?*1%NyFY;b8hj(??L4lF)f|>(-l3JV--hZJhbJ65(anf9!7m{?W0Q zknM~wmrFc9^`C~gmnd@bZp#>2$)BtgQ=w`k^b;ruf4b`xszwz(KnXrPa^q11bkA)P z2hE}AaJgoAzyBV3B%lp~c=tmfNhcAXM(j`%_$8_At=@<4h<4($ zk0pqC+U)?L_uKj9?fiH;c15CYV~m0Q5$5wHW?n0r*-6vRv+69qJxky#5m=!KX&6Vw ymUd2}VzNx&pk2AiS(Q!|Uokh(Z>rFfY6*wL-(saQ$64%vkn`OP{ipvaPX6D%TmR4i diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crordl.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crordl.r deleted file mode 100644 index 36b7d00fa9fb1334859704f88c27bb94786e7089..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1775 zcmdF_6#>Rt5$G14AbRAOXat zr6p;)7C;dYury%sb@cR8aJGsGElw>ejwwpaD9y=^NzW{a@h?ctb4^K4^)D!n2`p7{3BV6I>W3jZOyy`MU?X2IEc^ z3Ml5GYscg>GAN)3%3AMPdEekP>(6D%5%ZgOi?c=__FL$lujBfQeZFm}dNgTwH_vLVbNa{ak^RXNapWkO~O$bao94iFEM{cJ>eT3vrDIarJX? zbpeZmwSd$Ihd73WLZcSsOpwi>WC)^R7?`^mK$s0G#tx-HSqUW03B-^fjNMY*#|Gxpre2~hJ zh+qbIxroF96?W)FIU@s814s`HcmNp%Fb*C%0VoGb2gm?nCqlFV$^@l!Z1RxO%7I$7 J1j1q<3jk^E(u@EA diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crpo.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crpo.r deleted file mode 100644 index 7e1dd6509e02234e01dd7f2dec386fd8cd6117a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2129 zcmb_dO=#3W6n#y6Wxnxx5U#kb79_rCWtZ|2QR+Be>f zzgXl5Aa%fM`e+-p0gQ4~2T)xW^S>N}QK<6>fFNWq2>6F%z-r9&<`AJYn1iuGvBVFZ z)hZ@2OwE{@wXR*&YI6-u(n(#c)`+B=nyN1t4O1)Vx=M^2#L&zp&@c204Wnji(}up# zr}NpKo-@2`8Pl^wp`wM^H9ZNhIr683Uu<$W#O7Toz^p@}HKFrKf zE-TWABC5&9gAe74hVR!o;6vc2eC^x(OKIcwt)2WY=ia7dz1n`;)qC+xL8_R`sdLQP zoyKiD!(#X!>s>!!y#n@-wubFnQk#iq?QZiir+|D7M+P6Q6^Iz z??Y3^TLbP^W$?K;4&A6T>O=wG4xo3U74c|f`$pwB;^V*s?C00V#~v*K+`(1g)U@n> zB*$qL=paSyR=rAQ=IW#xHK3jk?86G`TR0f4F(HAo(6~9BCC(0naU427*}D0D#|x6s z7>y&J_afr#%8%Ce@7u23Mz%lRacnO??LQs#%!C^e*3j&e4VzDW1%+`O{u9YI!gF99 zaOea9N~a&9CgYjRK^}h`cLMcp)Lm+aG)LKR&a3MUD&IX TS^_(Zvim*A|DW%1GdsX$` diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/crsuppl.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/crsuppl.r deleted file mode 100644 index 0735ab25908889f17b15001c2c69c8fbe0b71ee1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2263 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 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/delinv.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/delinv.r deleted file mode 100644 index 66b6ecbe14579ce3f676a66486201b04dff5a000..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2300 zcmb_e&1(}u6n~q3q?M|*dhnn)6cnK!q_u@8h*^`6tTfq>Y-$fV7@KVp+ipU#DLHA5 z{R2b_iWK!Ap1kQnsNh8p7C{O{J@laB$6b36;(NP0ar4y=@R2vad7m?R^JbFX_v_Oy z%7O|=2jFREUk~&E1O<`-VkWd0ms{va_0|dCa*bVf;eb3@(&t9ps3CH@AtXv+=Dm=J z7u7;BQAlP=3-gI|R!_)zRr9CPs+=z-V)5B}VJWgos~j03E*;wfyX|F8)-buccM>w;=(Zf?X# zR1QS_vG(o2>hd*)H_HOBI%Tfh{l+=<`QxXDfj{>=^H&FNe|U7?dGgi3nzT0Y;P6lG zY%XISHP^MkUHAKLpZldXI+1zZXx@t2>6fwX8tK`Y@2MxJ0M_r2MF`Z}+hsDnwlLoc zasY2D!=91C)3W6ChvOk{Jf#gH5iD~ha$QxQ?}iM}i767q)?IqLIA(Ki;9 zBHo}F4fuh!Nh?zu5)zXRZ;-)wPYlFcZXmuDhEK00(<5>OPQV~utVy_tT7W)e@}s-6 zH>y^5@~a{>+DlqWoy%%!s?k8QZQWHAw~#r#Uvvgx_G@zqku zXlsm9zfpuqt?bd?+zQaj{_BfruOKwrtJR7v|6SbBAuAo#f0ch3OUL1P8$Gx0goiET z(FP|fMpJ#zCnEi;5A_v~XVxD2QQ6xMfFFbQ(lZ?mZ{|6SS<4^WfRRuiNx%igy}%L0GBFd(6m&y(FB*8MCd| zS$8_hObo1FxEd-Mwe}+@F6K&_&SsOEmebjFm8A=6QdbL1&m=XbH@qD!}u<{xVRq8vO;4`vNTh diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/delitem.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/delitem.r deleted file mode 100644 index 19a151ca57c84f61402bdaf4a728f7024aef0c09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3105 zcmb_eO>7%g5S~p8xU`T61VX@J1*ihi1}CE679`?0i@j3pHEV~ak|l!VZE7_MmVZzr zBx-TW5s8a$P^pI=dxHZ)IrhMhCB)AO^#UqARXvo##eCoHzT|nmn+l1M=FPmBdGqsT z_Gyp&`2D+I?--r{j>@&vv9zQ`iU#*0)qupTyD>6~?mR4#$;>ZhumDf>y5~=3At7Y5 zQp(w7Gk>PO+VR?*di(tgz0HgDjb^v*w7hMrvEezbPQBXtpxy1%bFEg@Yk%mq>m6UP zkXgud+Z&yF!`p0jy{);HIe+5BtELU!3{2*HKBMKf_JBzLh{!D<`4>|n73kApg)f7D zONP)s4bHAQ+3cy)C+9T}W9&T~8^Xo?;JRjTbz0lRuoE9YgVbeTJlM*WaV?UO0Hm4Tsu#G*{bGU zyHc~AvQsYR%FDTx6$f%_*|i*Z*>WK#p{U@v=jIi&P_0;bJGXMyvJ1sp$u7TBJI5Gy z3Er6r%VMTTL3pL)i5yYlgFwn30#c5*#i#h*e2Uk?r$<3)cPyljg!EBR`k4Xp{4DS> zAU)2=lk$wbB(F#opYYq_$eO$-o?P_xB85p zp2o+q>S$apqf17gXF?l;%+2+WfBGX7IM7AUc{h0?fH=MTm(mL6Z}R-z-vfOgWKusG z^)>&4FppouiMau1d}CO$Qbmsb37ln{6J{&lytZ)g{r#b=YO7 zj6g6CW<)CTIwbpkCbWToZQcsYSP$xFZT_2=P=C_B)YhuPf5u>0YHs^Xxwi%1B3Bto19Yd)?I4sDK`(@Sli7?!Yq9d7J9SC=34c7XP>ZYWh_0FZWd^82|tP diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/delord.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/delord.r deleted file mode 100644 index d384de28ad531c03fb8d2c3e284992078b4d7fd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3352 zcmb_fL2MgU5S?{GaiBB-Boqk=mL4Kd)5;07m?D84d#M-MUbBAIr&6l~AQ}Kq?1NdqI&}K@Uh~-v0lSpUv7;B}V#x=D+zf ze|Bd6dOh*W&mVr@vqEAzLA&F})08GESy&SZ$r9$x1PsuQ$)Y=fV@3U)MxdS zdcspIze~HUeT^koc-idPw@%NooJbGZY>M4Hif$MicE^lGg4iviwY#(h|;g%GN5*^eG2^|>HRR4Aoj1ryGz{M~ zi=+&{o4MEgt50-s0f2Qu!HNy@B-PU#&o=wKMw7I~vga2azgn*p>LOqkEsOcU5hb?< z&Izfw!hu-G`@ZY=ZdC++y%4xwRn)w|@hd{)%Z~7!+Jajy2cBDmF?i%n1o@yYKFa$} z$*T(oXZcRi4cJ|gJB!0Y#2iY*Ap=9Jf*(`-N#;mBzhfdkp*Y{aXw7erh#zMTAI~U$ zLgABYJ*C$0G)dFkO{Zv{-k}^hRG~Uuq!sG&9uR!vJlx$;^fD`TeIX~&Fnk5U#t8NO z@?WA?9!AfE)EJatU}&$iy2oPyk#iDteamhYsdodX!mQ@I1?Pj{iW&Ot4>8PC8Gj6Cy#KFi89S9UhvlYhei2rb)33KC}8TQ+q53 zw{zES{iy(tb#c4!E-WM^>HUASmTSyn-2U%Bq`uGWsD3c$EB|{O!vtRn+zU`n#UxkB z^O(_*i@YDz)aRq_noQi*AoDbuiK&D}VZt3q^SH}?`wDwiak=B@{H%hWkExK{SiZ^P z7^mEWeB*d+-!h&z%ZeQpo9R~epKU^s{3g%Z&k>~p(nhWTaSx88Cl7jB2C%6Mkl^SX zWBQz&@|TJEO*n_G^f?yy|LAjUg`7rzXzy+z6Y>7s<&v=J;Srfi-MwWVl@}TwXnOozAV3* zC0OS7D2GP0%flotZXfF%OiU#ny1)@&Oy)gbV%&UzV4DnrUkohw05IW`N8?y;0Yigx dsUtj!3>x`j!{tF?bn@lrZSMd2JAq^N=|4$INH+ig diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/delordl.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/delordl.r deleted file mode 100644 index 67347713ad47074ac26352d4615f96797b935d8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2071 zcmb_dO=uHQ5S~qIO-0m$l&T=BhhA*UwziPwVt;mN18p{DHxh9V3j3NCW4de->8a)- z^dc1WP^?1nBzUO@@#4Xw7sVo;M2H8ao(1heh%>wUlFeq*A8=rH=6&*+bBA~urw@{>|D_>S$@ej z^MjT+)ZafMsDtRG%q9K0K?JEc)L2|aKQ&O;wuMwG4>pt}X=>>NyLwY_*X zlwiDEFxfm_1!gcnZC}5e*sP**dCQ$PmZygGS-U4xd}#vpvs+pD{KHF9F&NBaJB^I6 zc>2%Y@!#Lwcm+*seyvvV)$cy#&OK#}^BhbZ@`jJ=xTy#)?)c{VH zDHng86bC&k2LIptU?dX#CTC6N7yIIIl4#jsR3Vmu`Y! SoX!vz_f{YOZ?7q@OZFe#0Qp$} diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/delsup.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/delsup.r deleted file mode 100644 index b5a070c21f1472cf4977cef78a475c29937ed5e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5560 zcmb_gO>7%Q6n<+PNTGjF3K631f=X4IwsL<-D-dzxrEy7Y*LF%OMIvdlZ31m-*-q69 zt+_xXkmxa0FOd*Z#VI#>;E)4{TtJl&QjfWSiiA)vKn_UZ`(|hC*|n3jz>{{~yf^cH z=grLSuKV$aGv8iwd;x5gxy1So=@992NQ)E$)TX`JhDX-N&x<6J2Tml>;5jl?yRfet z9Kx}r45TtyxA*w)VAZcwhbyDwQVOxMD8KV5gln4CciDy&xzbZFH_8QcON;n?|`ks?k`acYSw}UYr;(2BHDE8mcBWw z-)5>}-3-l1h^;;r5~_a{A|>V;`kD?upc`y69L4!U?qtC$u3Q#wNMBUEm3G7pF^_)v z_l}WIJ6}9>;%j%^@7r&6eX*fy{BkOJ{Z8+9ci(@|m>if1Ry?F2OswfIOW<)}9!X9> zz~qhncCZqEI=nDS^qXD3j&0j5vf($9&dIR8l{5vjI^fR?bbcTkd|xi>dO{>9y0WVuB z7W*>!)BtDZbCBxq&859`rl@2C`wWL3!n81z>=QqsF-w&6QySxJQ8ad+!cS}b3@~-A z*YpNp>e{I3O`1lQJ4D=H`L(IjkR-0?XhPymV+?3tq36Ipc9y ztxIXJu_5jCFnwC)dXP5jLC$8`CMdfG+(6IdgZ*}}5`TmSBEWo{PmApNUfF)Ee9S># z8PA(o$%nxGnXHG5WJ=xuP`;4q^-h=0np*URN?sPFsrV{&`5Yx!=|OHxF6adFVRAyZ z*=JseS*PT&en^`-<;X_af|}VbU09_sEH8)YFcp*4$Kz{^F9X6FMLq$;kw12P4jhxu zZaJW45kw65j=~9UJH(N2VtA$|$NUQull~Z)QBX5+jv&}sj4k4sW8i>!)vnxVQAE({ z zS=T;|19vw~wDf{l7z*Q!seHFgYGF94o?_y*j4Z;QeFw2l2`X6%E3(lzut?DEHu!k6 zQP6`nZZfW-o5fUmeYYWU8+??Af(5j^Kjuv$G@oeBIAif1wQKbAEVe;qPS=HYNTIn? z&AOmjBj2fgaN*xN1j&6#c=!muC9IfuPacAtskcQc(O~nJ3=^33!+VLa8D3J>)K9<- z+96%wHt$jUoAkt&BLdvVE&B&^=HCtH$;kIuv6hWLJj^uoAfc{FDtIGS-^{Tw-h&mq zWHZr^qy8d(iG$qC_J{p4^7k#jX5m){57!6;d&!M(Mcx3%=&=XQ#|e47BWytJCz!iH zCr5%pnKm25o~-*vfsLIzTHg%t8g?LM>~L4EZpYQOh}JG$N0NGT7_knl*OC>h(eYo- zi5ONh0BT!+cG8OtE79|l_mo}#t>bZ7?R<_yzd4_2w^P^WkiKgcfIIOlVUsUvUbn`a z-wyD#sUZ5E#;>7vbt+>Ysr%;|^fGEAU$^!{AN-oL_Kr58Ysxib1G7$zo(410=`;tI z<_C4ksxkLwuoi38sj-vS`g$UI+t`6cV~2IRx*gZ@+tirFigX>Bj@0QQ{F$|UGCH^? z35K54a%SHczPY7()a;<4&k1K2h)%q-24Kvt#5a;SA>a*To^E&*t=q&Jb)CTdxmsOw z+^*~6u(maDAk>-|Io=r>2?!Gt-#^SfWq6#i8sF5vv0>)7<8#b$X3#eCxf2uwbijK-TkfL04eT;wc7+10>??mi6@}#O5XN7f-J9{AC z^xxzu8rMF|;@ukMp91f&;=hL0ZPXP#2BQJdxSIL+ckvtLQN7%m?ef)}-@g7!Lq#z^ zyqcZ-ROS5mXw}bE#{7yD{PX_ARlNF}iSGH)Nq6!}%{}M4_?PC_{IUH~8uu%{J5hB< zYwjg~v|4koO!{s<=hnu-fi^ASPr4PYa6dj%`|(l1uhpr`K}(v1DsbbyPA7t_v%8?_ zKH#$MN0I&FID$bIrwt|+!@G%2T@(C;5*?qz*eo7(Vd6XprzjAd2T}(T$Aie!(ZRcc l_W3eMzqs^#Vc?eFM&~ADynI=*U*v+{Zax3k?@nlM!ar2b=i2}P diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/ref_call.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/ref_call.r deleted file mode 100644 index e72554097959bb69f72a29b46a17a82950e59b16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2109 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 diff --git a/SportsApp/Sports/build-output/AppServer/sports2020trgs/writem.r b/SportsApp/Sports/build-output/AppServer/sports2020trgs/writem.r deleted file mode 100644 index 9c5e3b57f2f7c912f5a15d8fe1dfac61a37b7a1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6313 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 diff --git a/SportsApp/Sports/build-output/tests/AppServer/CustomerTest.r b/SportsApp/Sports/build-output/tests/AppServer/CustomerTest.r deleted file mode 100644 index 099f7b958323a5307e17cc4189b6f71323ba84b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12461 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 GjweDxLqtfAdY4D)H_V(e z0ksX0MuKJqg2i!h>eN^DTe{smZky-lf#PhwJT2DW2Hn@&;Puy=@2}tA2C8;Dv7(e& z%KoKlx-lG$CwJ|~h)<=IqAayqW92OLlj+RY^m;hV-JzyNEUT^i!c$9M1l4_+#BA{P zIvCsx2Cr{7H#Y-$=L$CFap8t@$DF_7o41=+{NnBAAW?CfuZq~cJS;a#;&o)^FO3qs ztQ1vM)bS^M@@c3l+Mk;K%i#a&4@E95ITd+otBmQPC9RpQ>A|RSqM@qCNgh~5;W-s^ z;)+*^mv;5nrY%gJ$8xS4j%oa;Kk<}uv;F6ByLow1GS{Aqj&}NR9sQh7%RI`Y&tzl` zt(mXt^{C8)QdJGof6u+J@`cZMnfCQd)6Q6t#`-)k969OfPn+%YX1!Q_ApZ*brK=T| z>Xah1MLg0YtFVHA{8PG738zg(;GzDZh@F^aIXlsR=L@E%;n=}%yYJk;MI{Z%H|yyt)OjtLX>m!w~I z`~&}MisL$px860|r|t5&=|8~b{;5%at~xRgAuiQJui!yU{)eN;{|xm<|2lar;QxF& z@A<#Fb2C$>Vd3iN-;RC}&r`*ESUf>}U-g}JVE_a_}{Z;fojxwh5D<~ zm~!+l_rpA;{uRvY*S~g!Ip(461km60|5LN}9v&MdYo1n0x_Ws&d5SCOw@2I(jzIl6 z8yV2=L4RBSU`YPwHT1Xj_bc*$Pk(K~KV$T-tM&bZ{2}-Jz1e=~pv(1>e=Ic|=1C^} zkqo_(`q!vORgV6Zt$&Jw>Mzj0;>167P5FcTbW#7V|DnHF?Y0X{sP%5USv@Pav6pxL zgUj`YSJWiAxKBc(e7F4wPFBT4z0r|r z&T84LcZ&7-GvPw_b-8}zKS%zw{cBVI7S;^;1O3Q<>R*BSxB8L))W7}wnfMtSr_{e^ z=I{JvwOjtaYt{=qK|2q+PjI<@JFg`2Um*XbU+JMg^l1K4{s)*}G0VX}=-)DSY<7Pp zKgs{dpJE=uKb`vXmuCCzkIWNAly_}&{1YzMuVu)8K2t@MaUb>H&oB7D<%h%k@-e@N zA5Q02T{G%m%Kwx4AM$hlxVu$Em)!P`a25T?f2u$9yE^G_^K-9%XryiaQ~gPQ#ZLJX z{*i6|^z|S46K@_Y(FW=D`IDEVzZ7Yv62IArH?zTSL&{I$H}Es^KP*!bfZtB@AN*`W ze=(=|+wsqPFTmo7l*oqy9jxke@Aow)!2BI_Te1{c^JRKkkOpe3N^pv&toFXXoh{LjG8x*K4B>C_+oKk(aekG#5mH5=(%rTXUxNEw1Z!T-cR zZT`dwufhM|AL2J&PW>J~nC3qJ$HYHf{i!AJ2+?wPvbG$b;_~`yxh6?}PW#ss{Mqt@ zf&GQ}UnTwoKcIhU|APLw#}D;{fq!^BMt=7A?dX5HKm4rx2R{@42=p%pKSz80bbrA9 zPW$7Ye;n|e4}NC-{Y!#>Or7>ex}S9YbMSxeo}0b&CtP0tNF)XV2-~79aX*Q`Kf(U~ zfc#g78u*9yU$xEuqDoKn6aUC`U;pm>GNuuoewx+h=?f94`1ZVOz8t-C9sQT??-lqN z{E7Vw{MM~M{IlQRKkD9V)Ia3855TTjq8 z;D6k&;Qy|FydU<)^8@U1|AbjpSQ7rn{fZ%fY{#ej58WRE?C)`MPxBY|H(%J;-%s>c d@DJ@TMNIkGxj!I$e%QZg4NkU;&;QRq{{ce+GT8tC From fd9be03d764ca15ebcc3eea0553ff54b00751e51 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 18:47:55 +0530 Subject: [PATCH 109/135] files rename --- devpas/build-image/response.ini | 176 ++++++++++++++++++ .../build-script}/Dockerfile | 0 .../build-script}/build.sh | 0 .../build-script}/config.properties | 0 .../build-script}/hook-script.sh | 0 .../build-script}/license/.gitignore | 0 .../build-script}/validate.sh | 0 7 files changed, 176 insertions(+) create mode 100644 devpas/build-image/response.ini rename {Sports2020Db/buildScript => sports-2020-db/build-script}/Dockerfile (100%) rename {Sports2020Db/buildScript => sports-2020-db/build-script}/build.sh (100%) rename {Sports2020Db/buildScript => sports-2020-db/build-script}/config.properties (100%) rename {Sports2020Db/buildScript => sports-2020-db/build-script}/hook-script.sh (100%) rename {Sports2020Db/buildScript => sports-2020-db/build-script}/license/.gitignore (100%) rename {Sports2020Db/buildScript => sports-2020-db/build-script}/validate.sh (100%) diff --git a/devpas/build-image/response.ini b/devpas/build-image/response.ini new file mode 100644 index 0000000..6eb19f7 --- /dev/null +++ b/devpas/build-image/response.ini @@ -0,0 +1,176 @@ +; +; DESCRIPTION of Configuration Count +; +; NumberofConfigurations - the number of products being installed. +; + +[Configuration Count] +NumberofConfigurations=4 + +[Product Configuration 1] +name=PROGRESS +serial= +version=12.8ALPHA +control= +prodname=4GL Development System + +[Product Configuration 2] +name=Progress Software +serial= +version=12.8ALPHA +control= +prodname=Client Networking + +[Product Configuration 3] +name=Progress Software +serial= +version=12.8ALPHA +control= +prodname=OE Adv. Ent. RDBMS + +[Product Configuration 4] +name=Progress Software +serial= +version=12.8ALPHA +control= +prodname=Progress Dev AS for OE + +; +; DESCRIPTION of OpenEdge Explorer +; +; enable - indicates whether or not you want to enable the OpenEdge Explorer functionality. +; - a value of false indicates you do NOT want to enable the OpenEdge Explorer functionality. +; - a value of true indicates you want to enable the OpenEdge Explorer functionality. +; + +[OpenEdge Explorer] +enable=true + +; +; DESCRIPTION of Java +; +; JavaHome - the root directory where the JRE is installed +; + +[Java] +JavaHome=/usr/java + +; +; DESCRIPTION of Type and Destination +; +; type - identifies the type of installation. Valid values are COMPLETE and CUSTOM. +; path - identifies the directory in which you install your OpenEdge product software. +; workpath - identifies the directory in which your applications, databases, and log files will reside. +; oem_path - identifies the directory in which you install your Management product software. +; oem_workpath - identifies the directory in which your Management applications, databases, and log files will reside. +; + +[Type and Destination] +type=COMPLETE +path=/psc/dlc +workpath=/psc/wrk +oem_path=/psc/oemgmt +oem_workpath=/psc/wrk_oemgmt + +; +; DESCRIPTION of Server Engine +; +; UseSqlServerEngine - valid values are 0 and 1. +; 0 - indicates that the SQL Database Engine is to not be installed. +; 1 - indicates that the SQL Database Engine is to be installed. +; + +[Server Engine] +UseSqlServerEngine=1 + +; +; DESCRIPTION of Language Default +; +; DefaultLanguage - identifies the language in which PROMSGS appears by default. +; -Valid values are: +; Czech +; Dutch +; English - American +; English - International +; French +; German +; Italian +; Polish +; Portuguese +; Portuguese - Brazilian +; Spanish +; Spanish - Latin +; Swedish +; + +[Language Default] +DefaultLanguage=English - International + +; +; DESCRIPTION of Language Choice +; +; lang1, lang2, lang3 ... - Identifies all the PROMSGS languages installed during installation including the default language. +; + +[Language Choice] +lang1=English - International + +; +; DESCRIPTION of International Settings +; +; NOTE: For specific information please refer to the intlsets.txt file located at the root level of the cdrom from which this information is derived. +; cpinternal - identifies the -cpinternal and -cpstream values included in the startup.pf file. +; cpcollation - identifies the -cpcoll value included in the startup.pf file. +; cpcase - identifies the -cpcase value included in the startup.pf file. +; dateformat - identifies the -d value included in the startup.pf file. +; numsep - identifies the -numsep value included in the startup.pf file. +; numdec - identifies the -numdec value included in the startup.pf file. +; +; The following is a table of the numbers and the separators they represent: +; 32 - space +; 36 - dollar +; 39 - apostrophe +; 44 - comma +; 46 - period +; + +[International Settings] +cpinternal=ISO8859-1 +cpcollation=Basic +cpcase=Basic +dateformat=mdy +numsep=44 +numdec=46 +; +; DESCRIPTION of PacificAppServerPortDetails +; +; nPortHttp - A port for HTTP connections. +; nPortHttps - A port for HTTPS connections. +; nPortShutdown - Port on which the shutdown process will run. +; + +[PacificAppServerPortDetails] +nPortHttp=8810 +nPortHttps=8811 +nPortShutdown=8812 + +; +; DESCRIPTION of STS Key Plugin Dialog +; +; EnableAuthGatewaySTSClient - Enable Authentication Gateway. Enable = 1, Disable = 0 +; OEAuthGatewayURL - Gateway URL. +; ServerKeyPassword - Password +; STSKeystorePath- Path for storing keys. +; NoHostVerify - Enable = 1, Disable = 0 +; EnableAdminSrvSTSKeyPlugin - Enable = 1, Disable = 0 +; PollingInterVal - Polling interval (in minutes). +; + +[STS Key Plugin Dialog] +EnableAuthGatewaySTSClient=0 +OEAuthGatewayURL= +ServerKeyPassword= +STSKeystorePath= +NoHostVerify=0 +EnableAdminSrvSTSKeyPlugin=0 +PollingInterVal= diff --git a/Sports2020Db/buildScript/Dockerfile b/sports-2020-db/build-script/Dockerfile similarity index 100% rename from Sports2020Db/buildScript/Dockerfile rename to sports-2020-db/build-script/Dockerfile diff --git a/Sports2020Db/buildScript/build.sh b/sports-2020-db/build-script/build.sh similarity index 100% rename from Sports2020Db/buildScript/build.sh rename to sports-2020-db/build-script/build.sh diff --git a/Sports2020Db/buildScript/config.properties b/sports-2020-db/build-script/config.properties similarity index 100% rename from Sports2020Db/buildScript/config.properties rename to sports-2020-db/build-script/config.properties diff --git a/Sports2020Db/buildScript/hook-script.sh b/sports-2020-db/build-script/hook-script.sh similarity index 100% rename from Sports2020Db/buildScript/hook-script.sh rename to sports-2020-db/build-script/hook-script.sh diff --git a/Sports2020Db/buildScript/license/.gitignore b/sports-2020-db/build-script/license/.gitignore similarity index 100% rename from Sports2020Db/buildScript/license/.gitignore rename to sports-2020-db/build-script/license/.gitignore diff --git a/Sports2020Db/buildScript/validate.sh b/sports-2020-db/build-script/validate.sh similarity index 100% rename from Sports2020Db/buildScript/validate.sh rename to sports-2020-db/build-script/validate.sh From f86f0e63511c3ef49421849c1779fd2eacd00e8b Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 18:53:45 +0530 Subject: [PATCH 110/135] rename folder --- {sports-2020-db => sports-db}/build-script/Dockerfile | 0 {sports-2020-db => sports-db}/build-script/build.sh | 0 {sports-2020-db => sports-db}/build-script/config.properties | 0 {sports-2020-db => sports-db}/build-script/hook-script.sh | 0 {sports-2020-db => sports-db}/build-script/license/.gitignore | 0 {sports-2020-db => sports-db}/build-script/validate.sh | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {sports-2020-db => sports-db}/build-script/Dockerfile (100%) rename {sports-2020-db => sports-db}/build-script/build.sh (100%) rename {sports-2020-db => sports-db}/build-script/config.properties (100%) rename {sports-2020-db => sports-db}/build-script/hook-script.sh (100%) rename {sports-2020-db => sports-db}/build-script/license/.gitignore (100%) rename {sports-2020-db => sports-db}/build-script/validate.sh (100%) diff --git a/sports-2020-db/build-script/Dockerfile b/sports-db/build-script/Dockerfile similarity index 100% rename from sports-2020-db/build-script/Dockerfile rename to sports-db/build-script/Dockerfile diff --git a/sports-2020-db/build-script/build.sh b/sports-db/build-script/build.sh similarity index 100% rename from sports-2020-db/build-script/build.sh rename to sports-db/build-script/build.sh diff --git a/sports-2020-db/build-script/config.properties b/sports-db/build-script/config.properties similarity index 100% rename from sports-2020-db/build-script/config.properties rename to sports-db/build-script/config.properties diff --git a/sports-2020-db/build-script/hook-script.sh b/sports-db/build-script/hook-script.sh similarity index 100% rename from sports-2020-db/build-script/hook-script.sh rename to sports-db/build-script/hook-script.sh diff --git a/sports-2020-db/build-script/license/.gitignore b/sports-db/build-script/license/.gitignore similarity index 100% rename from sports-2020-db/build-script/license/.gitignore rename to sports-db/build-script/license/.gitignore diff --git a/sports-2020-db/build-script/validate.sh b/sports-db/build-script/validate.sh similarity index 100% rename from sports-2020-db/build-script/validate.sh rename to sports-db/build-script/validate.sh From 213bf618b92d1934abbfc6046165a13635c91f11 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 19:48:31 +0530 Subject: [PATCH 111/135] Compile job fixes --- .github/workflows/development.yml | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index ad6bd1e..facf574 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -13,10 +13,10 @@ jobs: - uses: actions/checkout@v3 - name: Pull Dev PASOE Docker Image run: | - docker login ec2-54-80-142-101.compute-1.amazonaws.com:9443 -u admin -p ${{ secrets.DOCKER_PWD }} - docker pull ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-dev-pasoe:12.8.0 + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker pull ${{ vars.DOCKER_REPO_URL }}/openedge-dev-pasoe:${{ vars.OE_VERSION }} - name: Running Gradle build in a Container - run: docker run --rm -v ./:/psc/wrk/Sports/ -e APP_LOCATION=/psc/wrk/Sports ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-dev-pasoe:12.8.0 + run: docker run --rm -v ./:/psc/wrk/Sports/ -e APP_LOCATION=/psc/wrk/Sports ${{ vars.DOCKER_REPO_URL }}/openedge-dev-pasoe:${{ vars.OE_VERSION }} - name: Setup Python - needed by publish step uses: actions/setup-python@v4 with: @@ -26,15 +26,7 @@ jobs: if: always() with: files: ${{ github.workspace }}/Sports/build/test-results/test/*.xml - check_name: ABL Unit Test Results for Sports App - # - uses: actions/upload-artifact@v2 # upload test results - # if: success() || failure() # run this step even if previous step failed - # with: - # name: test-results - # path: ./build/test-results/test/results.xml - # - name: upload zip file to nexus - # run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz - # working-directory: ./SportsApp + check_name: Test - ABL Unit Test Results for Sports App build: name: Build Docker Image for Sports App needs: compile From 2b79072433d0e6904d5729d1b6e57e381db1807d Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 20:06:45 +0530 Subject: [PATCH 112/135] Variables for docker build of sports app --- .github/workflows/development.yml | 12 +++++++++--- Sports/docker/build.sh | 2 +- Sports/docker/test.sh | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index facf574..24ed85e 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -40,8 +40,14 @@ jobs: run: cp -rf ./../build/distributions/ablapps/*.oear ./ablapps - name: Docker build run: sh build.sh + env: + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} - name: Test Docker Image state - Goss run: sh test.sh + env: + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} - name: Setup Python - needed by publish step uses: actions/setup-python@v4 with: @@ -53,9 +59,9 @@ jobs: check_name: Goss Test Results for Sports App - name: Push Docker Image to Develop Docker Registry run: | - docker tag sports:latest ec2-54-80-142-101.compute-1.amazonaws.com:9443/sports:latest - docker login ec2-54-80-142-101.compute-1.amazonaws.com:9443 -u admin -p ${{ secrets.DOCKER_PWD }} - docker push ec2-54-80-142-101.compute-1.amazonaws.com:9443/sports:latest + docker tag ${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} ${{ DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} + docker login ${{ DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker push ${{ DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} buildwebui: name: Build Docker Image for Web UI App permissions: write-all diff --git a/Sports/docker/build.sh b/Sports/docker/build.sh index 4da1d74..c485ab7 100644 --- a/Sports/docker/build.sh +++ b/Sports/docker/build.sh @@ -1,5 +1,5 @@ #!/bin/bash echo "Building Docker Image" -docker build -t sports:latest . --no-cache +docker build -t ${APP_NAME}:${APP_VERSION} . --no-cache echo "Building Docker Image Complete!" \ No newline at end of file diff --git a/Sports/docker/test.sh b/Sports/docker/test.sh index b70da92..668a170 100644 --- a/Sports/docker/test.sh +++ b/Sports/docker/test.sh @@ -8,4 +8,4 @@ cd ./tests # export GOSS_PATH=./goss export GOSS_OPTS="--format junit" echo "Running Goss Tests" -dgoss run -it sports:latest /bin/sh > unitTestReport.xml +dgoss run -it ${APP_NAME}:${APP_VERSION} /bin/sh > unitTestReport.xml From d69bf41341b0f98b83110b2d6febe3bf26256f86 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 20:08:51 +0530 Subject: [PATCH 113/135] fix var --- .github/workflows/development.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 24ed85e..d10a755 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -59,9 +59,9 @@ jobs: check_name: Goss Test Results for Sports App - name: Push Docker Image to Develop Docker Registry run: | - docker tag ${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} ${{ DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} - docker login ${{ DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} - docker push ${{ DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} + docker tag ${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} buildwebui: name: Build Docker Image for Web UI App permissions: write-all From 87db254b5fcd65771ce0796cfe1eb7fa4061c554 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 20:26:24 +0530 Subject: [PATCH 114/135] use variables for web app --- .github/workflows/development.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index d10a755..b1d16cf 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -26,7 +26,7 @@ jobs: if: always() with: files: ${{ github.workspace }}/Sports/build/test-results/test/*.xml - check_name: Test - ABL Unit Test Results for Sports App + check_name: Test Results - ABL Unit tests for Sports App build: name: Build Docker Image for Sports App needs: compile @@ -56,7 +56,7 @@ jobs: uses: EnricoMi/publish-unit-test-result-action/composite@v2 with: files: ${{ github.workspace }}/Sports/docker/tests/*.xml - check_name: Goss Test Results for Sports App + check_name: Test Results - Goss tests for Sports App - name: Push Docker Image to Develop Docker Registry run: | docker tag ${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} @@ -82,12 +82,12 @@ jobs: uses: EnricoMi/publish-unit-test-result-action/composite@v2 with: files: ${{ github.workspace }}/webui/tests/*.xml - check_name: Goss Test Results for Web UI App + check_name: Test Results - Goss tests for Web UI App - name: Push Docker Image to Develop Docker Registry run: | - docker tag webui:latest ec2-54-80-142-101.compute-1.amazonaws.com:9443/webui:latest - docker login ec2-54-80-142-101.compute-1.amazonaws.com:9443 -u admin -p ${{ secrets.DOCKER_PWD }} - docker push ec2-54-80-142-101.compute-1.amazonaws.com:9443/webui:latest + docker tag webui:latest ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest deploy: name: Test Sample App Deploy needs: [build, buildwebui] From 6f141482f5402d730b55e544c0e71065e2c99df7 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 20:46:37 +0530 Subject: [PATCH 115/135] use env vars for deploy --- .github/workflows/development.yml | 86 +++++-------------------------- deploy/deploy.sh | 8 +++ deploy/docker-compose.yml | 10 ++-- deploy/undeploy.sh | 7 +++ 4 files changed, 33 insertions(+), 78 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index b1d16cf..1533ab6 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -99,11 +99,23 @@ jobs: steps: - run: mkdir -p ./license - name: Download the OpenEdge License file - run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./license/progress.cfg --no-check-certificate + run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/${{vars.OE_VERSION}}/linux-${{vars.OE_VERSION}}-license.cfg > ./license/progress.cfg --no-check-certificate - name: Undeploy previous version of Sample App run: sudo sh undeploy.sh + env: + DOCKER_REPO_URL: ${{ vars.DOCKER_REPO_URL }} + OE_VERSION: ${{ vars.OE_VERSION }} + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} + APP_GROUP: ${{ vars.APP_GROUP }} - name: Deploy new version of Sample App run: sudo sh deploy.sh + env: + DOCKER_REPO_URL: ${{ vars.DOCKER_REPO_URL }} + OE_VERSION: ${{ vars.OE_VERSION }} + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} + APP_GROUP: ${{ vars.APP_GROUP }} - name: Test the app run: sh test-app-image.sh working-directory: ${{ github.workspace }}/test-app-image @@ -153,75 +165,3 @@ jobs: steps: - name: Publish Docker Images to Release Docker Registry run: echo "TODO" - # deploy: - # name: Sample App Deploy - # needs: [build, buildwebui] - # permissions: write-all - # runs-on: self-hosted - # defaults: - # run: - # working-directory: ${{vars.SAMPLEAPP_DIR}} - # steps: - # - name: Clean-up - # run: rm -Rf * - # - name: Download the application artifact - # run: wget ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz --no-check-certificate - # - name: Extract the artifact - # run: tar -zxf ${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz - # - run: mkdir ./deploy/license - # - name: Download the OpenEdge License file - # run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate - # - name: Undeploy previous version of Sample App - # run: sudo sh undeploy.sh - # working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy - # - name: Deploy new version of Sample App - # run: sudo sh deploy.sh - # working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy - # - name: Run security scan for Sample App docker image - # uses: aquasecurity/trivy-action@master - # with: - # image-ref: 'docker.io/sports:latest' - # format: 'sarif' - # output: 'trivy-results.sarif' - # - name: Upload security scan report of Sample App docker image to GitHub Security tab - # uses: github/codeql-action/upload-sarif@v2 - # with: - # sarif_file: 'trivy-results.sarif' - # report: - # name: Publish Reports - # permissions: write-all - # needs: compile - # if: always() - # runs-on: self-hosted - # steps: - # - name: Retrieve saved test report - # uses: actions/download-artifact@v2 - # with: - # name: test-results - # path: test-results/test - # # - uses: dorny/test-reporter@v1.6.0 - # # with: - # # name: ABL Unit Test Results # Name of the check run which will be created - # # path: test-results/test/results.xml # Path to test results - # # reporter: java-junit # Format of test results - # - name: Setup Python - # uses: actions/setup-python@v4 - # with: - # python-version: 3.8 - # - name: Publish Test Results - # uses: EnricoMi/publish-unit-test-result-action/composite@v2 - # with: - # files: test-results/**/*.xml -# test: -# name: Test deploy of Sample App -# needs: deploy -# runs-on: self-hosted -# defaults: -# run: -# working-directory: /opt/sampleapp -# steps: -# - name: Check application life -# run: wget -cO - https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports/rest/SportsService/Customer > customer.json --no-check-certificate - - - diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 580996c..aa6f08a 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -1,5 +1,13 @@ #!/bin/bash + +# env vars +export DOCKER_REPO_URL=${DOCKER_REPO_URL} +export OE_VERSION=${OE_VERSION} +export APP_NAME=${APP_NAME} +export APP_VERSION=${APP_VERSION} +export APP_GROUP=${APP_GROUP} + # create the app docker image # docker build --no-cache -t sports:latest . diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 6651c3a..2fa2ddd 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.6" services: oedbmachine: - image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-db:12.8.0 + image: ${DOCKER_REPO_URL}/openedge-db:${OE_VERSION} ports: - "7654:7654" - "7664-7684:7664-7684" @@ -10,7 +10,7 @@ services: - DB_MINPORT=7664 - DB_MAXPORT=7684 webuiapp: - image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/webui:latest + image: ${DOCKER_REPO_URL}/${APP_GROUP}/webui:latest volumes: - webui_dc:/webui web: @@ -26,17 +26,17 @@ services: volumes: - jdk_dc:/opt/java/openjdk ablapp: - image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/sports:latest + image: ${DOCKER_REPO_URL}:9443/${APP_GROUP}/${APP_NAME}:${APP_VERSION} volumes: - app_dc:/deploy-staging/artifacts pasoeinstance: - image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0 + image: ${DOCKER_REPO_URL}/openedge-pasoe:${OE_VERSION} depends_on: - jdk - ablapp environment: - FLUENTBIT_LOGGING=true - - APP_NAME=sports + - APP_NAME=${APP_NAME} - INSTANCE_NAME=oepas1 ports: - "8811:8811" diff --git a/deploy/undeploy.sh b/deploy/undeploy.sh index 4136638..4f61b81 100644 --- a/deploy/undeploy.sh +++ b/deploy/undeploy.sh @@ -1,5 +1,12 @@ #!/bin/bash +# env vars +export DOCKER_REPO_URL=${DOCKER_REPO_URL} +export OE_VERSION=${OE_VERSION} +export APP_NAME=${APP_NAME} +export APP_VERSION=${APP_VERSION} +export APP_GROUP=${APP_GROUP} + # undeploy PAS_INSTANCE_NAME=oepas1 docker-compose -p ${PAS_INSTANCE_NAME} down -v From 9e521ec93251224d2ca3ea7f25b6007e62b08179 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 21:00:01 +0530 Subject: [PATCH 116/135] debug and vars in stage --- .github/workflows/development.yml | 4 ++-- deploy/docker-compose.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 1533ab6..1b7424e 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -139,7 +139,7 @@ jobs: - name: Run security scan for Sports App docker image uses: aquasecurity/trivy-action@master with: - image-ref: 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/sports:latest' + image-ref: ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} format: 'sarif' output: 'trivy-results-sports.sarif' - name: Upload security scan report of Sports App docker image to GitHub Security tab @@ -150,7 +150,7 @@ jobs: - name: Run security scan for Web UI App docker image uses: aquasecurity/trivy-action@master with: - image-ref: 'ec2-54-80-142-101.compute-1.amazonaws.com:9443/webui:latest' + image-ref: ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest format: 'sarif' output: 'trivy-results-webui.sarif' - name: Upload security scan report of Web UI App docker image to GitHub Security tab diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 2fa2ddd..1eb71c7 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -26,7 +26,7 @@ services: volumes: - jdk_dc:/opt/java/openjdk ablapp: - image: ${DOCKER_REPO_URL}:9443/${APP_GROUP}/${APP_NAME}:${APP_VERSION} + image: ${DOCKER_REPO_URL}/${APP_GROUP}/${APP_NAME}:${APP_VERSION} volumes: - app_dc:/deploy-staging/artifacts pasoeinstance: From 0176b001a5573617be207602e3796c2af4151c3b Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 21:10:31 +0530 Subject: [PATCH 117/135] debug and vars in stage --- .github/workflows/development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 1b7424e..9da9c01 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -127,7 +127,7 @@ jobs: uses: EnricoMi/publish-unit-test-result-action/composite@v2 with: files: ${{ github.workspace }}/test-app-image/*.xml - check_name: Test - Results for Sports App Services + check_name: Test Results - Sports App Services - name: Undeploy the app and clean up resources run: echo "TODO" securityscans: From 378b9dda941d009dd57fa8ef73e392dd025c9e02 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 21:18:57 +0530 Subject: [PATCH 118/135] debug deploy --- deploy/deploy.sh | 16 +++++++++++----- deploy/undeploy.sh | 10 +++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index aa6f08a..19484b9 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -2,11 +2,17 @@ # env vars -export DOCKER_REPO_URL=${DOCKER_REPO_URL} -export OE_VERSION=${OE_VERSION} -export APP_NAME=${APP_NAME} -export APP_VERSION=${APP_VERSION} -export APP_GROUP=${APP_GROUP} +# export DOCKER_REPO_URL=${DOCKER_REPO_URL} +# export OE_VERSION=${OE_VERSION} +# export APP_NAME=${APP_NAME} +# export APP_VERSION=${APP_VERSION} +# export APP_GROUP=${APP_GROUP} + +# export APP_GROUP=progress/rahulk +# export APP_NAME=sports +# export APP_VERSION=latest +# export DOCKER_REPO_URL=ec2-54-80-142-101.compute-1.amazonaws.com:9443 +# export OE_VERSION=12.8.0 # create the app docker image # docker build --no-cache -t sports:latest . diff --git a/deploy/undeploy.sh b/deploy/undeploy.sh index 4f61b81..783d983 100644 --- a/deploy/undeploy.sh +++ b/deploy/undeploy.sh @@ -1,11 +1,11 @@ #!/bin/bash # env vars -export DOCKER_REPO_URL=${DOCKER_REPO_URL} -export OE_VERSION=${OE_VERSION} -export APP_NAME=${APP_NAME} -export APP_VERSION=${APP_VERSION} -export APP_GROUP=${APP_GROUP} +# export DOCKER_REPO_URL=${DOCKER_REPO_URL} +# export OE_VERSION=${OE_VERSION} +# export APP_NAME=${APP_NAME} +# export APP_VERSION=${APP_VERSION} +# export APP_GROUP=${APP_GROUP} # undeploy PAS_INSTANCE_NAME=oepas1 From 0fe55e071efbf363685eb283769d653eb71c4dc4 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 21:25:43 +0530 Subject: [PATCH 119/135] debug deploy --- deploy/deploy.sh | 19 ++++++------------- deploy/undeploy.sh | 13 ++++++------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 19484b9..c2d11f2 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -2,22 +2,15 @@ # env vars -# export DOCKER_REPO_URL=${DOCKER_REPO_URL} -# export OE_VERSION=${OE_VERSION} -# export APP_NAME=${APP_NAME} -# export APP_VERSION=${APP_VERSION} -# export APP_GROUP=${APP_GROUP} - -# export APP_GROUP=progress/rahulk -# export APP_NAME=sports -# export APP_VERSION=latest -# export DOCKER_REPO_URL=ec2-54-80-142-101.compute-1.amazonaws.com:9443 -# export OE_VERSION=12.8.0 +export DOCKER_REPO_URL=${DOCKER_REPO_URL} +export OE_VERSION=${OE_VERSION} +export APP_NAME=${APP_NAME} +export APP_VERSION=${APP_VERSION} +export APP_GROUP=${APP_GROUP} # create the app docker image # docker build --no-cache -t sports:latest . # deploy PAS_INSTANCE_NAME=oepas1 -docker-compose -p ${PAS_INSTANCE_NAME} up -d -echo "PASOE instance named '${PAS_INSTANCE_NAME}_dc' will be available at 'https://localhost:8811'" \ No newline at end of file +docker-compose -p ${PAS_INSTANCE_NAME} up -d --verbose \ No newline at end of file diff --git a/deploy/undeploy.sh b/deploy/undeploy.sh index 783d983..d4b03cf 100644 --- a/deploy/undeploy.sh +++ b/deploy/undeploy.sh @@ -1,13 +1,12 @@ #!/bin/bash # env vars -# export DOCKER_REPO_URL=${DOCKER_REPO_URL} -# export OE_VERSION=${OE_VERSION} -# export APP_NAME=${APP_NAME} -# export APP_VERSION=${APP_VERSION} -# export APP_GROUP=${APP_GROUP} +export DOCKER_REPO_URL=${DOCKER_REPO_URL} +export OE_VERSION=${OE_VERSION} +export APP_NAME=${APP_NAME} +export APP_VERSION=${APP_VERSION} +export APP_GROUP=${APP_GROUP} # undeploy PAS_INSTANCE_NAME=oepas1 -docker-compose -p ${PAS_INSTANCE_NAME} down -v -echo "Removed '${PAS_INSTANCE_NAME}_dc'" \ No newline at end of file +docker-compose -p ${PAS_INSTANCE_NAME} down -v --verbose \ No newline at end of file From 6a2948d2da017bd94d014a19a427446957ce94ae Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 21:30:59 +0530 Subject: [PATCH 120/135] debug deploy --- deploy/deploy.sh | 2 +- deploy/undeploy.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index c2d11f2..3247a38 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -13,4 +13,4 @@ export APP_GROUP=${APP_GROUP} # deploy PAS_INSTANCE_NAME=oepas1 -docker-compose -p ${PAS_INSTANCE_NAME} up -d --verbose \ No newline at end of file +docker-compose --verbose -p ${PAS_INSTANCE_NAME} up -d diff --git a/deploy/undeploy.sh b/deploy/undeploy.sh index d4b03cf..4bdcebd 100644 --- a/deploy/undeploy.sh +++ b/deploy/undeploy.sh @@ -9,4 +9,4 @@ export APP_GROUP=${APP_GROUP} # undeploy PAS_INSTANCE_NAME=oepas1 -docker-compose -p ${PAS_INSTANCE_NAME} down -v --verbose \ No newline at end of file +docker-compose --verbose -p ${PAS_INSTANCE_NAME} down -v \ No newline at end of file From 3b4e649d609612b7a82e87620f4b28fd37ef30e8 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 21:37:37 +0530 Subject: [PATCH 121/135] debug deploy --- deploy/deploy.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 3247a38..2ce243b 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -8,6 +8,12 @@ export APP_NAME=${APP_NAME} export APP_VERSION=${APP_VERSION} export APP_GROUP=${APP_GROUP} +echo "DOCKER_REPO_URL=${DOCKER_REPO_URL}" +echo "OE_VERSION=${OE_VERSION}" +echo "APP_NAME=${APP_NAME}" +echo "APP_VERSION=${APP_VERSION}" +echo "APP_GROUP=${APP_GROUP}" + # create the app docker image # docker build --no-cache -t sports:latest . From 7bc8122fb37ecd7af5d5fd40a1ed08ebbbab7b63 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 21:44:58 +0530 Subject: [PATCH 122/135] debug deploy --- .github/workflows/development.yml | 4 ++-- deploy/deploy.sh | 10 +++++----- deploy/undeploy.sh | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 9da9c01..750435c 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -101,7 +101,7 @@ jobs: - name: Download the OpenEdge License file run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/${{vars.OE_VERSION}}/linux-${{vars.OE_VERSION}}-license.cfg > ./license/progress.cfg --no-check-certificate - name: Undeploy previous version of Sample App - run: sudo sh undeploy.sh + run: sh undeploy.sh env: DOCKER_REPO_URL: ${{ vars.DOCKER_REPO_URL }} OE_VERSION: ${{ vars.OE_VERSION }} @@ -109,7 +109,7 @@ jobs: APP_VERSION: ${{ vars.APP_VERSION }} APP_GROUP: ${{ vars.APP_GROUP }} - name: Deploy new version of Sample App - run: sudo sh deploy.sh + run: sh deploy.sh env: DOCKER_REPO_URL: ${{ vars.DOCKER_REPO_URL }} OE_VERSION: ${{ vars.OE_VERSION }} diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 2ce243b..0ac9438 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -2,11 +2,11 @@ # env vars -export DOCKER_REPO_URL=${DOCKER_REPO_URL} -export OE_VERSION=${OE_VERSION} -export APP_NAME=${APP_NAME} -export APP_VERSION=${APP_VERSION} -export APP_GROUP=${APP_GROUP} +# export DOCKER_REPO_URL=${DOCKER_REPO_URL} +# export OE_VERSION=${OE_VERSION} +# export APP_NAME=${APP_NAME} +# export APP_VERSION=${APP_VERSION} +# export APP_GROUP=${APP_GROUP} echo "DOCKER_REPO_URL=${DOCKER_REPO_URL}" echo "OE_VERSION=${OE_VERSION}" diff --git a/deploy/undeploy.sh b/deploy/undeploy.sh index 4bdcebd..c472b9f 100644 --- a/deploy/undeploy.sh +++ b/deploy/undeploy.sh @@ -9,4 +9,4 @@ export APP_GROUP=${APP_GROUP} # undeploy PAS_INSTANCE_NAME=oepas1 -docker-compose --verbose -p ${PAS_INSTANCE_NAME} down -v \ No newline at end of file +docker-compose --verbose -p ${PAS_INSTANCE_NAME} down -v \ No newline at end of file From 952f3e9c5c344420791701a5dc15606b32355555 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 21:54:36 +0530 Subject: [PATCH 123/135] debug deploy --- deploy/deploy.sh | 7 ------- deploy/undeploy.sh | 10 +++++----- test-app-image/test/testCustomer.js | 4 ++-- webui/src/grid.js | 2 +- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 0ac9438..61154ed 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -1,13 +1,6 @@ #!/bin/bash - # env vars -# export DOCKER_REPO_URL=${DOCKER_REPO_URL} -# export OE_VERSION=${OE_VERSION} -# export APP_NAME=${APP_NAME} -# export APP_VERSION=${APP_VERSION} -# export APP_GROUP=${APP_GROUP} - echo "DOCKER_REPO_URL=${DOCKER_REPO_URL}" echo "OE_VERSION=${OE_VERSION}" echo "APP_NAME=${APP_NAME}" diff --git a/deploy/undeploy.sh b/deploy/undeploy.sh index c472b9f..ab46740 100644 --- a/deploy/undeploy.sh +++ b/deploy/undeploy.sh @@ -1,11 +1,11 @@ #!/bin/bash # env vars -export DOCKER_REPO_URL=${DOCKER_REPO_URL} -export OE_VERSION=${OE_VERSION} -export APP_NAME=${APP_NAME} -export APP_VERSION=${APP_VERSION} -export APP_GROUP=${APP_GROUP} +echo "DOCKER_REPO_URL=${DOCKER_REPO_URL}" +echo "OE_VERSION=${OE_VERSION}" +echo "APP_NAME=${APP_NAME}" +echo "APP_VERSION=${APP_VERSION}" +echo "APP_GROUP=${APP_GROUP}" # undeploy PAS_INSTANCE_NAME=oepas1 diff --git a/test-app-image/test/testCustomer.js b/test-app-image/test/testCustomer.js index 87dad75..445cc83 100644 --- a/test-app-image/test/testCustomer.js +++ b/test-app-image/test/testCustomer.js @@ -15,8 +15,8 @@ var result, describe("Test Customer", () => { const options = { - serviceURI: "https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports", - catalogURI: "https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports/static/SportsService.json", + serviceURI: "https://ec2-18-206-124-116.compute-1.amazonaws.com:8811/Sports", + catalogURI: "https://ec2-18-206-124-116.compute-1.amazonaws.com:8811/Sports/static/SportsService.json", resourceName: "Customer", authenticationModel: "anonymous" }; diff --git a/webui/src/grid.js b/webui/src/grid.js index e6c9df0..8871a6d 100644 --- a/webui/src/grid.js +++ b/webui/src/grid.js @@ -4,7 +4,7 @@ $(function () { 'use strict'; - var serviceURI = "https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports"; + var serviceURI = "https://ec2-18-206-124-116.compute-1.amazonaws.com:8811/Sports"; var catalogURI = serviceURI + "/static/SportsService.json"; function createGrid() { From e98b6faebe425c911af3b6f69a71a5906133c5fd Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Sep 2023 22:32:18 +0530 Subject: [PATCH 124/135] stage tested artifacts --- .github/workflows/development.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 750435c..8426d5a 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -164,4 +164,10 @@ jobs: runs-on: self-hosted steps: - name: Publish Docker Images to Release Docker Registry - run: echo "TODO" + run: | + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker tag ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest-tested + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest-tested + docker tag ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }}-tested + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }}-tested + From 8af0e68f5598db8f46195b4343384ff02ef3fe6f Mon Sep 17 00:00:00 2001 From: Rahul Kumar <45960461+rahulk3010@users.noreply.github.com> Date: Thu, 21 Sep 2023 01:06:08 +0530 Subject: [PATCH 125/135] Sports app base reference (#9) * Use OEDF default plugin * Sports - init project * merge prop change * using default OEDF plugin * docker build for sports app * docker image for webui app * webui script changes * dockerfile for sports * deploy script changes - use webui as docker image * sports app gradle scripts * goss for webui * goss command fix * new flow for build sports app * fix workflow syntax * fix build errors * debug * debug * debug * fix report publish issues * trigger build * fix workspace var * remove modes from goss yaml * take care of exit code * test result name * docker build for webui * docker build for webui * Push docker images to nexus * change in deploy scripts * mkdir fix * Security scan job * Placer holder job for push to release * add dependency web app container * clean up * gradle build in container * debug * fix dev yml * Test for app * Use correct node * Fixing test for Sample app * Fixing test for Sample app * Fixing test for Sample app * delete unwanted folders * files rename * rename folder * Compile job fixes * Variables for docker build of sports app * fix var * use variables for web app * use env vars for deploy * debug and vars in stage * debug and vars in stage * debug deploy * debug deploy * debug deploy * debug deploy * debug deploy * debug deploy * stage tested artifacts --- .github/workflows/development.yml | 217 ++++++++++------ .gitignore | 6 + {SportsApp/Sports => Sports}/.dbconnection | 2 +- {SportsApp/Sports => Sports}/.gitignore | 3 + {SportsApp/Sports => Sports}/.project | 0 {SportsApp/Sports => Sports}/.propath | 2 +- .../.services/AppServer/Customer.pidl | 0 .../.services/AppServer/Item.pidl | 0 .../.services/AppServer/Order.pidl | 0 .../.services/AppServer/Salesrep.pidl | 0 .../.services/AppServer/State.pidl | 0 .../rest/SportsService/resourceModel.xml | 0 .../Expose/rest/SportsService/spring.xml | 0 .../.services/Sports.properties | 0 .../Sports => Sports}/.services/adapters.pamf | 0 .../org.eclipse.wst.common.component | 0 ....eclipse.wst.common.project.facet.core.xml | 0 .../.settings/pasoe.appserver.settings.xml | 0 .../.settings/pasoe.config.xml | 0 .../Sports => Sports}/AppServer/Customer.cls | 0 .../AppServer/GetCustOrders.p | 0 .../Sports => Sports}/AppServer/Item.cls | 0 .../Sports => Sports}/AppServer/Order.cls | 0 .../Sports => Sports}/AppServer/Salesrep.cls | 0 .../Sports => Sports}/AppServer/State.cls | 0 .../AppServer/UpdateCustOrders.p | 0 .../Sports => Sports}/AppServer/customer.i | 0 {SportsApp/Sports => Sports}/AppServer/item.i | 0 .../Sports => Sports}/AppServer/myCustOrder.i | 0 .../Sports => Sports}/AppServer/order.i | 0 .../Sports => Sports}/AppServer/salesrep.i | 0 .../AppServer/sports2020trgs/asordnum.p | 0 .../AppServer/sports2020trgs/asstate.p | 0 .../AppServer/sports2020trgs/crbin.p | 0 .../AppServer/sports2020trgs/crcust.p | 0 .../AppServer/sports2020trgs/cremp.p | 0 .../AppServer/sports2020trgs/crintr.p | 0 .../AppServer/sports2020trgs/crinv.p | 0 .../AppServer/sports2020trgs/critem.p | 0 .../AppServer/sports2020trgs/crlocdef.p | 0 .../AppServer/sports2020trgs/crord.p | 0 .../AppServer/sports2020trgs/crordl.p | 0 .../AppServer/sports2020trgs/crpo.p | 0 .../AppServer/sports2020trgs/crsuppl.p | 0 .../AppServer/sports2020trgs/crware.p | 0 .../AppServer/sports2020trgs/delcust.p | 0 .../AppServer/sports2020trgs/delinv.p | 0 .../AppServer/sports2020trgs/delitem.p | 0 .../AppServer/sports2020trgs/delord.p | 0 .../AppServer/sports2020trgs/delordl.p | 0 .../AppServer/sports2020trgs/delsup.p | 0 .../AppServer/sports2020trgs/ref_call.p | 0 .../AppServer/sports2020trgs/wrcust.p | 0 .../AppServer/sports2020trgs/writem.p | 0 .../AppServer/sports2020trgs/wrord.p | 0 .../AppServer/sports2020trgs/wrordl.p | 0 .../Sports => Sports}/AppServer/state.i | 0 .../PASOEContent/META-INF/MANIFEST.MF | 0 .../PASOEContent/WEB-INF/adapters/rest/README | 0 .../rest/_oepingService/_oepingService.paar | Bin .../WEB-INF/adapters/rest/runtime.props | 0 .../PASOEContent/WEB-INF/adapters/soap/README | 0 .../WEB-INF/adapters/soap/camel-spring.xml | 0 .../WEB-INF/adapters/web/README.txt | 0 .../WEB-INF/backup/apsv-basic.xml | 0 .../PASOEContent/WEB-INF/backup/apsv-none.xml | 0 .../backup/oeablSecurity-anonymous.xml | 0 .../backup/oeablSecurity-basic-ldap-ext.xml | 0 .../backup/oeablSecurity-basic-ldap.xml | 0 .../backup/oeablSecurity-basic-local.xml | 0 .../backup/oeablSecurity-basic-oerealm.xml | 0 .../backup/oeablSecurity-basic-saml.xml | 0 .../backup/oeablSecurity-container.xml | 0 .../backup/oeablSecurity-form-ldap-ext.xml | 0 .../backup/oeablSecurity-form-ldap.xml | 0 .../backup/oeablSecurity-form-local.xml | 0 .../backup/oeablSecurity-form-oerealm.xml | 0 .../backup/oeablSecurity-form-saml.xml | 0 .../WEB-INF/backup/soap-basic.xml | 0 .../PASOEContent/WEB-INF/backup/soap-none.xml | 0 .../PASOEContent/WEB-INF/classes/manifest.txt | 0 .../WEB-INF/home/ServerStatus.html | 0 .../WEB-INF/jsp/errorJSONBody.jsp | 0 .../PASOEContent/WEB-INF/jsp/errorPage.jsp | 0 .../WEB-INF/jsp/errorPageBody.jsp | 0 .../WEB-INF/jsp/errorPageFooter.jsp | 0 .../WEB-INF/jsp/errorPageHeader.jsp | 0 .../WEB-INF/jsp/exceptionPage.jsp | 0 .../WEB-INF/jsp/exceptionPageFooter.jsp | 0 .../WEB-INF/jsp/exceptionPageHeader.jsp | 0 .../WEB-INF/jsp/httpCodeDesc-terse.properties | 0 .../jsp/httpCodeDesc-verbose.properties | 0 .../WEB-INF/jsp/loadErrorData.jsp | 0 .../PASOEContent/WEB-INF/jsp/loginPage.jsp | 0 .../PASOEContent/WEB-INF/jsp/logoutPage.jsp | 0 .../PASOEContent/WEB-INF/logging.xml | 0 .../PASOEContent/WEB-INF/metadata/README | 0 .../PASOEContent/WEB-INF/oeablSecurity.csv | 0 .../WEB-INF/oeablSecurity.properties | 0 .../WEB-INF/oeablSecurity.properties.README | 0 .../PASOEContent/WEB-INF/oeablSecurity.xml | 0 .../PASOEContent/WEB-INF/oeablSecurityJWT.csv | 0 .../PASOEContent/WEB-INF/openedge/ReadMe.txt | 0 .../PASOEContent/WEB-INF/security.tld | 0 .../WEB-INF/spring/properties-loader.xml | 0 .../PASOEContent/WEB-INF/tlr/Merge.template | 0 .../PASOEContent/WEB-INF/tlr/oeabl_tlr | 0 .../PASOEContent/WEB-INF/users.properties | 0 .../PASOEContent/WEB-INF/web.xml | 0 .../PASOEContent/WEB-INF/web.xml-clientcert | 0 .../PASOEContent/favicon.ico | Bin .../Sports => Sports}/PASOEContent/index.jsp | 0 .../PASOEContent/static/ServerStatus.html | 0 .../PASOEContent/static/SportsService.json | 0 .../PASOEContent/static/auth/login.html | 0 .../PASOEContent/static/auth/login.jsp | 0 .../PASOEContent/static/auth/loginfail.html | 0 .../PASOEContent/static/auth/logout.html | 0 .../PASOEContent/static/auth/logout.jsp | 0 .../PASOEContent/static/commonPageFooter.html | 0 .../PASOEContent/static/commonPageHeader.html | 0 .../PASOEContent/static/commonStyle.css | 0 .../PASOEContent/static/error/error401.html | 0 .../PASOEContent/static/error/error403.html | 0 .../PASOEContent/static/error/error404.html | 0 .../PASOEContent/static/error/error500.html | 0 .../PASOEContent/static/error/error503.html | 0 .../PASOEContent/static/home.html | 0 .../PASOEContent/static/home.jsp | 0 .../PASOEContent/static/images/Thumbs.db | Bin .../PASOEContent/static/images/appserver.png | Bin .../PASOEContent/static/images/appserver2.png | Bin .../PASOEContent/static/images/background.png | Bin .../static/images/communities.png | Bin .../PASOEContent/static/images/content.png | Bin .../static/images/documentation.png | Bin .../PASOEContent/static/images/gear.png | Bin .../PASOEContent/static/images/progress.png | Bin .../PASOEContent/static/images/qstart.png | Bin .../PASOEContent/static/images/videos.png | Bin .../PASOEContent/static/index.jsp | 0 .../PASOEContent/static/serverstatus.js | 0 .../PASOEContent/static/sessionsExceeded.jsp | 0 {SportsApp/Sports => Sports}/build.config | 2 +- {SportsApp/Sports => Sports}/build.gradle | 152 +++++------- {SportsApp/Sports => Sports}/conf/MANIFEST.MF | 0 {SportsApp/Sports => Sports}/conf/startup.pf | 0 .../deploy => Sports/docker}/Dockerfile | 3 +- .../docker/ablapps}/.gitignore | 0 Sports/docker/build.sh | 5 + Sports/docker/test.sh | 11 + Sports/docker/tests/goss.yaml | 13 + .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 {SportsApp/Sports => Sports}/gradlew | 10 +- {SportsApp/Sports => Sports}/gradlew.bat | 0 {SportsApp => Sports}/settings.gradle | 3 +- .../tests/AppServer}/CustomerTest.cls | 0 .../tests/AppServer}/OrderTest.cls | 0 .../tests/AppServer}/SportsTestSuite.cls | 0 .../Sports => Sports}/tlr/merge.properties | 2 +- .../tlr/openedge.properties.merge | 0 SportsApp/.gitignore | 0 SportsApp/Sports/.gitattributes | 6 - .../Sports/.scripts/common-build-tasks.xml | 140 ----------- SportsApp/Sports/.scripts/deploy-app.sh | 27 -- .../.scripts/dotpropath-to-pctpropath.xsl | 41 --- SportsApp/Sports/.scripts/propath.xml | 38 --- SportsApp/Sports/.scripts/standardpaths.xml | 71 ------ SportsApp/Sports/gradle.properties | 2 - SportsApp/Sports/settings.gradle | 11 - SportsApp/build.gradle | 31 --- SportsApp/deploy/.gitignore | 2 - SportsApp/deploy/deploy.sh | 9 - SportsApp/deploy/undeploy.sh | 6 - SportsApp/gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - SportsApp/gradlew | 234 ------------------ SportsApp/gradlew.bat | 89 ------- deploy/deploy.sh | 15 ++ .../deploy => deploy}/docker-compose.yml | 39 +-- .../ablapps => deploy/license}/.gitignore | 0 .../deploy => deploy}/scripts/startServer.sh | 0 deploy/undeploy.sh | 12 + devpas/build-image/.gitignore | 1 + devpas/build-image/Dockerfile | 45 ++++ devpas/build-image/build.sh | 5 + devpas/build-image/response.ini | 176 +++++++++++++ devpas/build-image/scripts/initcmd.sh | 9 + .../build-script}/Dockerfile | 0 .../build-script}/build.sh | 0 .../build-script}/config.properties | 0 .../build-script}/hook-script.sh | 0 .../build-script}/license/.gitignore | 0 .../build-script}/validate.sh | 0 test-app-image/package.json | 21 ++ test-app-image/test-app-image.sh | 14 ++ test-app-image/test/testCustomer.js | 176 +++++++++++++ webui/Dockerfile | 17 ++ webui/build.sh | 5 + {SportsApp/webui => webui/src}/grid.js | 2 +- {SportsApp/webui => webui/src}/index.html | 2 +- .../webui => webui/src}/progress.all.js | 0 webui/test.sh | 11 + webui/tests/goss.yaml | 21 ++ 205 files changed, 818 insertions(+), 896 deletions(-) rename {SportsApp/Sports => Sports}/.dbconnection (63%) rename {SportsApp/Sports => Sports}/.gitignore (82%) rename {SportsApp/Sports => Sports}/.project (100%) rename {SportsApp/Sports => Sports}/.propath (94%) rename {SportsApp/Sports => Sports}/.services/AppServer/Customer.pidl (100%) rename {SportsApp/Sports => Sports}/.services/AppServer/Item.pidl (100%) rename {SportsApp/Sports => Sports}/.services/AppServer/Order.pidl (100%) rename {SportsApp/Sports => Sports}/.services/AppServer/Salesrep.pidl (100%) rename {SportsApp/Sports => Sports}/.services/AppServer/State.pidl (100%) rename {SportsApp/Sports => Sports}/.services/Expose/rest/SportsService/resourceModel.xml (100%) rename {SportsApp/Sports => Sports}/.services/Expose/rest/SportsService/spring.xml (100%) rename {SportsApp/Sports => Sports}/.services/Sports.properties (100%) rename {SportsApp/Sports => Sports}/.services/adapters.pamf (100%) rename {SportsApp/Sports => Sports}/.settings/org.eclipse.wst.common.component (100%) rename {SportsApp/Sports => Sports}/.settings/org.eclipse.wst.common.project.facet.core.xml (100%) rename {SportsApp/Sports => Sports}/.settings/pasoe.appserver.settings.xml (100%) rename {SportsApp/Sports => Sports}/.settings/pasoe.config.xml (100%) rename {SportsApp/Sports => Sports}/AppServer/Customer.cls (100%) rename {SportsApp/Sports => Sports}/AppServer/GetCustOrders.p (100%) rename {SportsApp/Sports => Sports}/AppServer/Item.cls (100%) rename {SportsApp/Sports => Sports}/AppServer/Order.cls (100%) rename {SportsApp/Sports => Sports}/AppServer/Salesrep.cls (100%) rename {SportsApp/Sports => Sports}/AppServer/State.cls (100%) rename {SportsApp/Sports => Sports}/AppServer/UpdateCustOrders.p (100%) rename {SportsApp/Sports => Sports}/AppServer/customer.i (100%) rename {SportsApp/Sports => Sports}/AppServer/item.i (100%) rename {SportsApp/Sports => Sports}/AppServer/myCustOrder.i (100%) rename {SportsApp/Sports => Sports}/AppServer/order.i (100%) rename {SportsApp/Sports => Sports}/AppServer/salesrep.i (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/asordnum.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/asstate.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/crbin.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/crcust.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/cremp.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/crintr.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/crinv.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/critem.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/crlocdef.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/crord.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/crordl.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/crpo.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/crsuppl.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/crware.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/delcust.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/delinv.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/delitem.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/delord.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/delordl.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/delsup.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/ref_call.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/wrcust.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/writem.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/wrord.p (100%) rename {SportsApp/Sports => Sports}/AppServer/sports2020trgs/wrordl.p (100%) rename {SportsApp/Sports => Sports}/AppServer/state.i (100%) rename {SportsApp/Sports => Sports}/PASOEContent/META-INF/MANIFEST.MF (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/adapters/rest/README (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/adapters/rest/_oepingService/_oepingService.paar (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/adapters/rest/runtime.props (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/adapters/soap/README (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/adapters/web/README.txt (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/apsv-basic.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/apsv-none.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/soap-basic.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/backup/soap-none.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/classes/manifest.txt (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/home/ServerStatus.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/errorPage.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/errorPageBody.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/exceptionPage.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/loadErrorData.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/loginPage.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/jsp/logoutPage.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/logging.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/metadata/README (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/oeablSecurity.csv (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/oeablSecurity.properties (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/oeablSecurity.properties.README (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/oeablSecurity.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/oeablSecurityJWT.csv (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/openedge/ReadMe.txt (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/security.tld (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/spring/properties-loader.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/tlr/Merge.template (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/tlr/oeabl_tlr (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/users.properties (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/web.xml (100%) rename {SportsApp/Sports => Sports}/PASOEContent/WEB-INF/web.xml-clientcert (100%) rename {SportsApp/Sports => Sports}/PASOEContent/favicon.ico (100%) rename {SportsApp/Sports => Sports}/PASOEContent/index.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/ServerStatus.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/SportsService.json (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/auth/login.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/auth/login.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/auth/loginfail.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/auth/logout.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/auth/logout.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/commonPageFooter.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/commonPageHeader.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/commonStyle.css (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/error/error401.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/error/error403.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/error/error404.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/error/error500.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/error/error503.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/home.html (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/home.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/images/Thumbs.db (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/images/appserver.png (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/images/appserver2.png (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/images/background.png (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/images/communities.png (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/images/content.png (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/images/documentation.png (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/images/gear.png (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/images/progress.png (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/images/qstart.png (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/images/videos.png (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/index.jsp (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/serverstatus.js (100%) rename {SportsApp/Sports => Sports}/PASOEContent/static/sessionsExceeded.jsp (100%) rename {SportsApp/Sports => Sports}/build.config (99%) rename {SportsApp/Sports => Sports}/build.gradle (51%) rename {SportsApp/Sports => Sports}/conf/MANIFEST.MF (100%) rename {SportsApp/Sports => Sports}/conf/startup.pf (100%) rename {SportsApp/deploy => Sports/docker}/Dockerfile (92%) rename {Sports2020Db/buildScript/license => Sports/docker/ablapps}/.gitignore (100%) create mode 100644 Sports/docker/build.sh create mode 100644 Sports/docker/test.sh create mode 100644 Sports/docker/tests/goss.yaml rename {SportsApp/Sports => Sports}/gradle/wrapper/gradle-wrapper.jar (100%) rename {SportsApp/Sports => Sports}/gradle/wrapper/gradle-wrapper.properties (100%) rename {SportsApp/Sports => Sports}/gradlew (95%) rename {SportsApp/Sports => Sports}/gradlew.bat (100%) rename {SportsApp => Sports}/settings.gradle (86%) rename {SportsApp/Sports/tests => Sports/tests/AppServer}/CustomerTest.cls (100%) rename {SportsApp/Sports/tests => Sports/tests/AppServer}/OrderTest.cls (100%) rename {SportsApp/Sports/tests => Sports/tests/AppServer}/SportsTestSuite.cls (100%) rename {SportsApp/Sports => Sports}/tlr/merge.properties (97%) rename {SportsApp/Sports => Sports}/tlr/openedge.properties.merge (100%) delete mode 100644 SportsApp/.gitignore delete mode 100644 SportsApp/Sports/.gitattributes delete mode 100644 SportsApp/Sports/.scripts/common-build-tasks.xml delete mode 100644 SportsApp/Sports/.scripts/deploy-app.sh delete mode 100644 SportsApp/Sports/.scripts/dotpropath-to-pctpropath.xsl delete mode 100644 SportsApp/Sports/.scripts/propath.xml delete mode 100644 SportsApp/Sports/.scripts/standardpaths.xml delete mode 100644 SportsApp/Sports/gradle.properties delete mode 100644 SportsApp/Sports/settings.gradle delete mode 100644 SportsApp/build.gradle delete mode 100644 SportsApp/deploy/.gitignore delete mode 100644 SportsApp/deploy/deploy.sh delete mode 100644 SportsApp/deploy/undeploy.sh delete mode 100644 SportsApp/gradle/wrapper/gradle-wrapper.jar delete mode 100644 SportsApp/gradle/wrapper/gradle-wrapper.properties delete mode 100644 SportsApp/gradlew delete mode 100644 SportsApp/gradlew.bat create mode 100644 deploy/deploy.sh rename {SportsApp/deploy => deploy}/docker-compose.yml (61%) rename {SportsApp/deploy/ablapps => deploy/license}/.gitignore (100%) rename {SportsApp/deploy => deploy}/scripts/startServer.sh (100%) create mode 100644 deploy/undeploy.sh create mode 100644 devpas/build-image/.gitignore create mode 100644 devpas/build-image/Dockerfile create mode 100644 devpas/build-image/build.sh create mode 100644 devpas/build-image/response.ini create mode 100644 devpas/build-image/scripts/initcmd.sh rename {Sports2020Db/buildScript => sports-db/build-script}/Dockerfile (100%) rename {Sports2020Db/buildScript => sports-db/build-script}/build.sh (100%) rename {Sports2020Db/buildScript => sports-db/build-script}/config.properties (100%) rename {Sports2020Db/buildScript => sports-db/build-script}/hook-script.sh (100%) rename {SportsApp/deploy => sports-db/build-script}/license/.gitignore (100%) rename {Sports2020Db/buildScript => sports-db/build-script}/validate.sh (100%) create mode 100644 test-app-image/package.json create mode 100644 test-app-image/test-app-image.sh create mode 100644 test-app-image/test/testCustomer.js create mode 100644 webui/Dockerfile create mode 100644 webui/build.sh rename {SportsApp/webui => webui/src}/grid.js (96%) rename {SportsApp/webui => webui/src}/index.html (96%) rename {SportsApp/webui => webui/src}/progress.all.js (100%) create mode 100644 webui/test.sh create mode 100644 webui/tests/goss.yaml diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index d6a3c80..8426d5a 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -3,92 +3,171 @@ run-name: ${{ github.actor }} is compiling our Sample App 🚀 on: [push] jobs: compile: - name: OpenEdge Compile job + name: OpenEdge Compile Job + permissions: write-all runs-on: self-hosted + defaults: + run: + working-directory: ${{ github.workspace }}/Sports steps: - - name: Clean-up - run: rm -Rf * - uses: actions/checkout@v3 - - run: sh gradlew clean build - working-directory: ./SportsApp - - uses: actions/upload-artifact@v2 # upload test results - if: success() || failure() # run this step even if previous step failed + - name: Pull Dev PASOE Docker Image + run: | + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker pull ${{ vars.DOCKER_REPO_URL }}/openedge-dev-pasoe:${{ vars.OE_VERSION }} + - name: Running Gradle build in a Container + run: docker run --rm -v ./:/psc/wrk/Sports/ -e APP_LOCATION=/psc/wrk/Sports ${{ vars.DOCKER_REPO_URL }}/openedge-dev-pasoe:${{ vars.OE_VERSION }} + - name: Setup Python - needed by publish step + uses: actions/setup-python@v4 with: - name: test-results - path: ./SportsApp/Sports/build/test-results/test/results.xml - - name: upload zip file to nexus - run: curl -v -k -u admin:${{secrets.DOCKER_PWD}} --upload-file ./build/distributions/sportsApp.tar.gz ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz - working-directory: ./SportsApp - deploy: - name: Sample App Deploy + python-version: 3.8 + - name: Publish ABL Unit Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + if: always() + with: + files: ${{ github.workspace }}/Sports/build/test-results/test/*.xml + check_name: Test Results - ABL Unit tests for Sports App + build: + name: Build Docker Image for Sports App needs: compile permissions: write-all runs-on: self-hosted defaults: run: - working-directory: ${{vars.SAMPLEAPP_DIR}} + working-directory: ${{ github.workspace }}/Sports/docker + steps: + - name: Copy ABLApp archive ('.oear') + run: cp -rf ./../build/distributions/ablapps/*.oear ./ablapps + - name: Docker build + run: sh build.sh + env: + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} + - name: Test Docker Image state - Goss + run: sh test.sh + env: + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} + - name: Setup Python - needed by publish step + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Publish Goss Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + with: + files: ${{ github.workspace }}/Sports/docker/tests/*.xml + check_name: Test Results - Goss tests for Sports App + - name: Push Docker Image to Develop Docker Registry + run: | + docker tag ${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} + buildwebui: + name: Build Docker Image for Web UI App + permissions: write-all + runs-on: self-hosted + defaults: + run: + working-directory: ${{ github.workspace }}/webui + steps: + - name: Docker build + run: sh build.sh + - name: Test Docker Image state - Goss + run: sh test.sh + - name: Setup Python - needed by publish step + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Publish Goss Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + with: + files: ${{ github.workspace }}/webui/tests/*.xml + check_name: Test Results - Goss tests for Web UI App + - name: Push Docker Image to Develop Docker Registry + run: | + docker tag webui:latest ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest + deploy: + name: Test Sample App Deploy + needs: [build, buildwebui] + permissions: write-all + runs-on: self-hosted + defaults: + run: + working-directory: ${{ github.workspace }}/deploy steps: - - name: Clean-up - run: rm -Rf * - - name: Download the application artifact - run: wget ${{vars.NEXUS_URL}}/com/progess/${{vars.SAMPLEAPP_NAME}}/${{vars.SAMPLEAPP_VERSION}}/${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz --no-check-certificate - - name: Extract the artifact - run: tar -zxf ${{vars.SAMPLEAPP_NAME}}-${{vars.SAMPLEAPP_VERSION}}.tar.gz - - run: mkdir ./deploy/license + - run: mkdir -p ./license - name: Download the OpenEdge License file - run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/12.8.0/linux-12.8.0-license.cfg > ./deploy/license/progress.cfg --no-check-certificate + run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/${{vars.OE_VERSION}}/linux-${{vars.OE_VERSION}}-license.cfg > ./license/progress.cfg --no-check-certificate - name: Undeploy previous version of Sample App - run: sudo sh undeploy.sh - working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy + run: sh undeploy.sh + env: + DOCKER_REPO_URL: ${{ vars.DOCKER_REPO_URL }} + OE_VERSION: ${{ vars.OE_VERSION }} + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} + APP_GROUP: ${{ vars.APP_GROUP }} - name: Deploy new version of Sample App - run: sudo sh deploy.sh - working-directory: ${{vars.SAMPLEAPP_DIR}}/deploy - - name: Run security scan for Sample App docker image + run: sh deploy.sh + env: + DOCKER_REPO_URL: ${{ vars.DOCKER_REPO_URL }} + OE_VERSION: ${{ vars.OE_VERSION }} + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} + APP_GROUP: ${{ vars.APP_GROUP }} + - name: Test the app + run: sh test-app-image.sh + working-directory: ${{ github.workspace }}/test-app-image + - name: Setup Python - needed by publish step + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Publish Sample Sports App Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + with: + files: ${{ github.workspace }}/test-app-image/*.xml + check_name: Test Results - Sports App Services + - name: Undeploy the app and clean up resources + run: echo "TODO" + securityscans: + name: Scan Docker Images for Security Vulnerabilities + needs: [build, buildwebui] + permissions: write-all + runs-on: self-hosted + steps: + - name: Run security scan for Sports App docker image uses: aquasecurity/trivy-action@master with: - image-ref: 'docker.io/sports:latest' + image-ref: ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} format: 'sarif' - output: 'trivy-results.sarif' - - name: Upload security scan report of Sample App docker image to GitHub Security tab + output: 'trivy-results-sports.sarif' + - name: Upload security scan report of Sports App docker image to GitHub Security tab uses: github/codeql-action/upload-sarif@v2 with: - sarif_file: 'trivy-results.sarif' - report: - name: Publish Reports - permissions: write-all - needs: compile - if: always() + sarif_file: 'trivy-results-sports.sarif' + category: sports-app-scan + - name: Run security scan for Web UI App docker image + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest + format: 'sarif' + output: 'trivy-results-webui.sarif' + - name: Upload security scan report of Web UI App docker image to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: 'trivy-results-webui.sarif' + category: webui-app-scan + stage: + name: Stage Artifacts and Docker Images for Release + needs: [deploy, securityscans] runs-on: self-hosted steps: - - name: Retrieve saved test report - uses: actions/download-artifact@v2 - with: - name: test-results - path: test-results/test - # - uses: dorny/test-reporter@v1.6.0 - # with: - # name: ABL Unit Test Results # Name of the check run which will be created - # path: test-results/test/results.xml # Path to test results - # reporter: java-junit # Format of test results - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action/composite@v2 - with: - files: test-results/**/*.xml -# test: -# name: Test deploy of Sample App -# needs: deploy -# runs-on: self-hosted -# defaults: -# run: -# working-directory: /opt/sampleapp -# steps: -# - name: Check application life -# run: wget -cO - https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports/rest/SportsService/Customer > customer.json --no-check-certificate - - - - + - name: Publish Docker Images to Release Docker Registry + run: | + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker tag ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest-tested + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest-tested + docker tag ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }}-tested + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }}-tested + diff --git a/.gitignore b/.gitignore index 0dbf1d8..c5a760c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,9 @@ gradle-app.setting # Ignore Gradle build output directory build + +tmp + +goss +dgoss +unitTestReport.xml \ No newline at end of file diff --git a/SportsApp/Sports/.dbconnection b/Sports/.dbconnection similarity index 63% rename from SportsApp/Sports/.dbconnection rename to Sports/.dbconnection index 0b808fd..5c94f99 100644 --- a/SportsApp/Sports/.dbconnection +++ b/Sports/.dbconnection @@ -1,4 +1,4 @@ - + diff --git a/SportsApp/Sports/.gitignore b/Sports/.gitignore similarity index 82% rename from SportsApp/Sports/.gitignore rename to Sports/.gitignore index 5a57b4e..bddab10 100644 --- a/SportsApp/Sports/.gitignore +++ b/Sports/.gitignore @@ -4,3 +4,6 @@ # Ignore Gradle build output directory build build-output + +results.xml +velocity.log \ No newline at end of file diff --git a/SportsApp/Sports/.project b/Sports/.project similarity index 100% rename from SportsApp/Sports/.project rename to Sports/.project diff --git a/SportsApp/Sports/.propath b/Sports/.propath similarity index 94% rename from SportsApp/Sports/.propath rename to Sports/.propath index 5ed6576..5ddc5cc 100644 --- a/SportsApp/Sports/.propath +++ b/Sports/.propath @@ -7,7 +7,7 @@ - + diff --git a/SportsApp/Sports/.services/AppServer/Customer.pidl b/Sports/.services/AppServer/Customer.pidl similarity index 100% rename from SportsApp/Sports/.services/AppServer/Customer.pidl rename to Sports/.services/AppServer/Customer.pidl diff --git a/SportsApp/Sports/.services/AppServer/Item.pidl b/Sports/.services/AppServer/Item.pidl similarity index 100% rename from SportsApp/Sports/.services/AppServer/Item.pidl rename to Sports/.services/AppServer/Item.pidl diff --git a/SportsApp/Sports/.services/AppServer/Order.pidl b/Sports/.services/AppServer/Order.pidl similarity index 100% rename from SportsApp/Sports/.services/AppServer/Order.pidl rename to Sports/.services/AppServer/Order.pidl diff --git a/SportsApp/Sports/.services/AppServer/Salesrep.pidl b/Sports/.services/AppServer/Salesrep.pidl similarity index 100% rename from SportsApp/Sports/.services/AppServer/Salesrep.pidl rename to Sports/.services/AppServer/Salesrep.pidl diff --git a/SportsApp/Sports/.services/AppServer/State.pidl b/Sports/.services/AppServer/State.pidl similarity index 100% rename from SportsApp/Sports/.services/AppServer/State.pidl rename to Sports/.services/AppServer/State.pidl diff --git a/SportsApp/Sports/.services/Expose/rest/SportsService/resourceModel.xml b/Sports/.services/Expose/rest/SportsService/resourceModel.xml similarity index 100% rename from SportsApp/Sports/.services/Expose/rest/SportsService/resourceModel.xml rename to Sports/.services/Expose/rest/SportsService/resourceModel.xml diff --git a/SportsApp/Sports/.services/Expose/rest/SportsService/spring.xml b/Sports/.services/Expose/rest/SportsService/spring.xml similarity index 100% rename from SportsApp/Sports/.services/Expose/rest/SportsService/spring.xml rename to Sports/.services/Expose/rest/SportsService/spring.xml diff --git a/SportsApp/Sports/.services/Sports.properties b/Sports/.services/Sports.properties similarity index 100% rename from SportsApp/Sports/.services/Sports.properties rename to Sports/.services/Sports.properties diff --git a/SportsApp/Sports/.services/adapters.pamf b/Sports/.services/adapters.pamf similarity index 100% rename from SportsApp/Sports/.services/adapters.pamf rename to Sports/.services/adapters.pamf diff --git a/SportsApp/Sports/.settings/org.eclipse.wst.common.component b/Sports/.settings/org.eclipse.wst.common.component similarity index 100% rename from SportsApp/Sports/.settings/org.eclipse.wst.common.component rename to Sports/.settings/org.eclipse.wst.common.component diff --git a/SportsApp/Sports/.settings/org.eclipse.wst.common.project.facet.core.xml b/Sports/.settings/org.eclipse.wst.common.project.facet.core.xml similarity index 100% rename from SportsApp/Sports/.settings/org.eclipse.wst.common.project.facet.core.xml rename to Sports/.settings/org.eclipse.wst.common.project.facet.core.xml diff --git a/SportsApp/Sports/.settings/pasoe.appserver.settings.xml b/Sports/.settings/pasoe.appserver.settings.xml similarity index 100% rename from SportsApp/Sports/.settings/pasoe.appserver.settings.xml rename to Sports/.settings/pasoe.appserver.settings.xml diff --git a/SportsApp/Sports/.settings/pasoe.config.xml b/Sports/.settings/pasoe.config.xml similarity index 100% rename from SportsApp/Sports/.settings/pasoe.config.xml rename to Sports/.settings/pasoe.config.xml diff --git a/SportsApp/Sports/AppServer/Customer.cls b/Sports/AppServer/Customer.cls similarity index 100% rename from SportsApp/Sports/AppServer/Customer.cls rename to Sports/AppServer/Customer.cls diff --git a/SportsApp/Sports/AppServer/GetCustOrders.p b/Sports/AppServer/GetCustOrders.p similarity index 100% rename from SportsApp/Sports/AppServer/GetCustOrders.p rename to Sports/AppServer/GetCustOrders.p diff --git a/SportsApp/Sports/AppServer/Item.cls b/Sports/AppServer/Item.cls similarity index 100% rename from SportsApp/Sports/AppServer/Item.cls rename to Sports/AppServer/Item.cls diff --git a/SportsApp/Sports/AppServer/Order.cls b/Sports/AppServer/Order.cls similarity index 100% rename from SportsApp/Sports/AppServer/Order.cls rename to Sports/AppServer/Order.cls diff --git a/SportsApp/Sports/AppServer/Salesrep.cls b/Sports/AppServer/Salesrep.cls similarity index 100% rename from SportsApp/Sports/AppServer/Salesrep.cls rename to Sports/AppServer/Salesrep.cls diff --git a/SportsApp/Sports/AppServer/State.cls b/Sports/AppServer/State.cls similarity index 100% rename from SportsApp/Sports/AppServer/State.cls rename to Sports/AppServer/State.cls diff --git a/SportsApp/Sports/AppServer/UpdateCustOrders.p b/Sports/AppServer/UpdateCustOrders.p similarity index 100% rename from SportsApp/Sports/AppServer/UpdateCustOrders.p rename to Sports/AppServer/UpdateCustOrders.p diff --git a/SportsApp/Sports/AppServer/customer.i b/Sports/AppServer/customer.i similarity index 100% rename from SportsApp/Sports/AppServer/customer.i rename to Sports/AppServer/customer.i diff --git a/SportsApp/Sports/AppServer/item.i b/Sports/AppServer/item.i similarity index 100% rename from SportsApp/Sports/AppServer/item.i rename to Sports/AppServer/item.i diff --git a/SportsApp/Sports/AppServer/myCustOrder.i b/Sports/AppServer/myCustOrder.i similarity index 100% rename from SportsApp/Sports/AppServer/myCustOrder.i rename to Sports/AppServer/myCustOrder.i diff --git a/SportsApp/Sports/AppServer/order.i b/Sports/AppServer/order.i similarity index 100% rename from SportsApp/Sports/AppServer/order.i rename to Sports/AppServer/order.i diff --git a/SportsApp/Sports/AppServer/salesrep.i b/Sports/AppServer/salesrep.i similarity index 100% rename from SportsApp/Sports/AppServer/salesrep.i rename to Sports/AppServer/salesrep.i diff --git a/SportsApp/Sports/AppServer/sports2020trgs/asordnum.p b/Sports/AppServer/sports2020trgs/asordnum.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/asordnum.p rename to Sports/AppServer/sports2020trgs/asordnum.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/asstate.p b/Sports/AppServer/sports2020trgs/asstate.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/asstate.p rename to Sports/AppServer/sports2020trgs/asstate.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crbin.p b/Sports/AppServer/sports2020trgs/crbin.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/crbin.p rename to Sports/AppServer/sports2020trgs/crbin.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crcust.p b/Sports/AppServer/sports2020trgs/crcust.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/crcust.p rename to Sports/AppServer/sports2020trgs/crcust.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/cremp.p b/Sports/AppServer/sports2020trgs/cremp.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/cremp.p rename to Sports/AppServer/sports2020trgs/cremp.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crintr.p b/Sports/AppServer/sports2020trgs/crintr.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/crintr.p rename to Sports/AppServer/sports2020trgs/crintr.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crinv.p b/Sports/AppServer/sports2020trgs/crinv.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/crinv.p rename to Sports/AppServer/sports2020trgs/crinv.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/critem.p b/Sports/AppServer/sports2020trgs/critem.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/critem.p rename to Sports/AppServer/sports2020trgs/critem.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crlocdef.p b/Sports/AppServer/sports2020trgs/crlocdef.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/crlocdef.p rename to Sports/AppServer/sports2020trgs/crlocdef.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crord.p b/Sports/AppServer/sports2020trgs/crord.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/crord.p rename to Sports/AppServer/sports2020trgs/crord.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crordl.p b/Sports/AppServer/sports2020trgs/crordl.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/crordl.p rename to Sports/AppServer/sports2020trgs/crordl.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crpo.p b/Sports/AppServer/sports2020trgs/crpo.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/crpo.p rename to Sports/AppServer/sports2020trgs/crpo.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crsuppl.p b/Sports/AppServer/sports2020trgs/crsuppl.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/crsuppl.p rename to Sports/AppServer/sports2020trgs/crsuppl.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/crware.p b/Sports/AppServer/sports2020trgs/crware.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/crware.p rename to Sports/AppServer/sports2020trgs/crware.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delcust.p b/Sports/AppServer/sports2020trgs/delcust.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/delcust.p rename to Sports/AppServer/sports2020trgs/delcust.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delinv.p b/Sports/AppServer/sports2020trgs/delinv.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/delinv.p rename to Sports/AppServer/sports2020trgs/delinv.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delitem.p b/Sports/AppServer/sports2020trgs/delitem.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/delitem.p rename to Sports/AppServer/sports2020trgs/delitem.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delord.p b/Sports/AppServer/sports2020trgs/delord.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/delord.p rename to Sports/AppServer/sports2020trgs/delord.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delordl.p b/Sports/AppServer/sports2020trgs/delordl.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/delordl.p rename to Sports/AppServer/sports2020trgs/delordl.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/delsup.p b/Sports/AppServer/sports2020trgs/delsup.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/delsup.p rename to Sports/AppServer/sports2020trgs/delsup.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/ref_call.p b/Sports/AppServer/sports2020trgs/ref_call.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/ref_call.p rename to Sports/AppServer/sports2020trgs/ref_call.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/wrcust.p b/Sports/AppServer/sports2020trgs/wrcust.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/wrcust.p rename to Sports/AppServer/sports2020trgs/wrcust.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/writem.p b/Sports/AppServer/sports2020trgs/writem.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/writem.p rename to Sports/AppServer/sports2020trgs/writem.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/wrord.p b/Sports/AppServer/sports2020trgs/wrord.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/wrord.p rename to Sports/AppServer/sports2020trgs/wrord.p diff --git a/SportsApp/Sports/AppServer/sports2020trgs/wrordl.p b/Sports/AppServer/sports2020trgs/wrordl.p similarity index 100% rename from SportsApp/Sports/AppServer/sports2020trgs/wrordl.p rename to Sports/AppServer/sports2020trgs/wrordl.p diff --git a/SportsApp/Sports/AppServer/state.i b/Sports/AppServer/state.i similarity index 100% rename from SportsApp/Sports/AppServer/state.i rename to Sports/AppServer/state.i diff --git a/SportsApp/Sports/PASOEContent/META-INF/MANIFEST.MF b/Sports/PASOEContent/META-INF/MANIFEST.MF similarity index 100% rename from SportsApp/Sports/PASOEContent/META-INF/MANIFEST.MF rename to Sports/PASOEContent/META-INF/MANIFEST.MF diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/README b/Sports/PASOEContent/WEB-INF/adapters/rest/README similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/README rename to Sports/PASOEContent/WEB-INF/adapters/rest/README diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/_oepingService/_oepingService.paar b/Sports/PASOEContent/WEB-INF/adapters/rest/_oepingService/_oepingService.paar similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/_oepingService/_oepingService.paar rename to Sports/PASOEContent/WEB-INF/adapters/rest/_oepingService/_oepingService.paar diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props b/Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props rename to Sports/PASOEContent/WEB-INF/adapters/rest/runtime.props diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/README b/Sports/PASOEContent/WEB-INF/adapters/soap/README similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/README rename to Sports/PASOEContent/WEB-INF/adapters/soap/README diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml b/Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml rename to Sports/PASOEContent/WEB-INF/adapters/soap/camel-spring.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/adapters/web/README.txt b/Sports/PASOEContent/WEB-INF/adapters/web/README.txt similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/adapters/web/README.txt rename to Sports/PASOEContent/WEB-INF/adapters/web/README.txt diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml b/Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml rename to Sports/PASOEContent/WEB-INF/backup/apsv-basic.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-none.xml b/Sports/PASOEContent/WEB-INF/backup/apsv-none.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/apsv-none.xml rename to Sports/PASOEContent/WEB-INF/backup/apsv-none.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-anonymous.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap-ext.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-ldap.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-local.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-oerealm.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-basic-saml.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-container.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap-ext.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-ldap.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-local.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-oerealm.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml b/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml rename to Sports/PASOEContent/WEB-INF/backup/oeablSecurity-form-saml.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-basic.xml b/Sports/PASOEContent/WEB-INF/backup/soap-basic.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-basic.xml rename to Sports/PASOEContent/WEB-INF/backup/soap-basic.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-none.xml b/Sports/PASOEContent/WEB-INF/backup/soap-none.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/backup/soap-none.xml rename to Sports/PASOEContent/WEB-INF/backup/soap-none.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/classes/manifest.txt b/Sports/PASOEContent/WEB-INF/classes/manifest.txt similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/classes/manifest.txt rename to Sports/PASOEContent/WEB-INF/classes/manifest.txt diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/home/ServerStatus.html b/Sports/PASOEContent/WEB-INF/home/ServerStatus.html similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/home/ServerStatus.html rename to Sports/PASOEContent/WEB-INF/home/ServerStatus.html diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp b/Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp rename to Sports/PASOEContent/WEB-INF/jsp/errorJSONBody.jsp diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp b/Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp rename to Sports/PASOEContent/WEB-INF/jsp/errorPage.jsp diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp b/Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp rename to Sports/PASOEContent/WEB-INF/jsp/errorPageBody.jsp diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp b/Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp rename to Sports/PASOEContent/WEB-INF/jsp/errorPageFooter.jsp diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp b/Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp rename to Sports/PASOEContent/WEB-INF/jsp/errorPageHeader.jsp diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp b/Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp rename to Sports/PASOEContent/WEB-INF/jsp/exceptionPage.jsp diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp b/Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp rename to Sports/PASOEContent/WEB-INF/jsp/exceptionPageFooter.jsp diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp b/Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp rename to Sports/PASOEContent/WEB-INF/jsp/exceptionPageHeader.jsp diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties b/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties rename to Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-terse.properties diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties b/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties rename to Sports/PASOEContent/WEB-INF/jsp/httpCodeDesc-verbose.properties diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp b/Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp rename to Sports/PASOEContent/WEB-INF/jsp/loadErrorData.jsp diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp b/Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp rename to Sports/PASOEContent/WEB-INF/jsp/loginPage.jsp diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp b/Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp rename to Sports/PASOEContent/WEB-INF/jsp/logoutPage.jsp diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/logging.xml b/Sports/PASOEContent/WEB-INF/logging.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/logging.xml rename to Sports/PASOEContent/WEB-INF/logging.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/metadata/README b/Sports/PASOEContent/WEB-INF/metadata/README similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/metadata/README rename to Sports/PASOEContent/WEB-INF/metadata/README diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.csv b/Sports/PASOEContent/WEB-INF/oeablSecurity.csv similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.csv rename to Sports/PASOEContent/WEB-INF/oeablSecurity.csv diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties b/Sports/PASOEContent/WEB-INF/oeablSecurity.properties similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties rename to Sports/PASOEContent/WEB-INF/oeablSecurity.properties diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README b/Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README rename to Sports/PASOEContent/WEB-INF/oeablSecurity.properties.README diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.xml b/Sports/PASOEContent/WEB-INF/oeablSecurity.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurity.xml rename to Sports/PASOEContent/WEB-INF/oeablSecurity.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv b/Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv rename to Sports/PASOEContent/WEB-INF/oeablSecurityJWT.csv diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt b/Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt rename to Sports/PASOEContent/WEB-INF/openedge/ReadMe.txt diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/security.tld b/Sports/PASOEContent/WEB-INF/security.tld similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/security.tld rename to Sports/PASOEContent/WEB-INF/security.tld diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/spring/properties-loader.xml b/Sports/PASOEContent/WEB-INF/spring/properties-loader.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/spring/properties-loader.xml rename to Sports/PASOEContent/WEB-INF/spring/properties-loader.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/tlr/Merge.template b/Sports/PASOEContent/WEB-INF/tlr/Merge.template similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/tlr/Merge.template rename to Sports/PASOEContent/WEB-INF/tlr/Merge.template diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr b/Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr rename to Sports/PASOEContent/WEB-INF/tlr/oeabl_tlr diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/users.properties b/Sports/PASOEContent/WEB-INF/users.properties similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/users.properties rename to Sports/PASOEContent/WEB-INF/users.properties diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/web.xml b/Sports/PASOEContent/WEB-INF/web.xml similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/web.xml rename to Sports/PASOEContent/WEB-INF/web.xml diff --git a/SportsApp/Sports/PASOEContent/WEB-INF/web.xml-clientcert b/Sports/PASOEContent/WEB-INF/web.xml-clientcert similarity index 100% rename from SportsApp/Sports/PASOEContent/WEB-INF/web.xml-clientcert rename to Sports/PASOEContent/WEB-INF/web.xml-clientcert diff --git a/SportsApp/Sports/PASOEContent/favicon.ico b/Sports/PASOEContent/favicon.ico similarity index 100% rename from SportsApp/Sports/PASOEContent/favicon.ico rename to Sports/PASOEContent/favicon.ico diff --git a/SportsApp/Sports/PASOEContent/index.jsp b/Sports/PASOEContent/index.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/index.jsp rename to Sports/PASOEContent/index.jsp diff --git a/SportsApp/Sports/PASOEContent/static/ServerStatus.html b/Sports/PASOEContent/static/ServerStatus.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/ServerStatus.html rename to Sports/PASOEContent/static/ServerStatus.html diff --git a/SportsApp/Sports/PASOEContent/static/SportsService.json b/Sports/PASOEContent/static/SportsService.json similarity index 100% rename from SportsApp/Sports/PASOEContent/static/SportsService.json rename to Sports/PASOEContent/static/SportsService.json diff --git a/SportsApp/Sports/PASOEContent/static/auth/login.html b/Sports/PASOEContent/static/auth/login.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/auth/login.html rename to Sports/PASOEContent/static/auth/login.html diff --git a/SportsApp/Sports/PASOEContent/static/auth/login.jsp b/Sports/PASOEContent/static/auth/login.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/static/auth/login.jsp rename to Sports/PASOEContent/static/auth/login.jsp diff --git a/SportsApp/Sports/PASOEContent/static/auth/loginfail.html b/Sports/PASOEContent/static/auth/loginfail.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/auth/loginfail.html rename to Sports/PASOEContent/static/auth/loginfail.html diff --git a/SportsApp/Sports/PASOEContent/static/auth/logout.html b/Sports/PASOEContent/static/auth/logout.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/auth/logout.html rename to Sports/PASOEContent/static/auth/logout.html diff --git a/SportsApp/Sports/PASOEContent/static/auth/logout.jsp b/Sports/PASOEContent/static/auth/logout.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/static/auth/logout.jsp rename to Sports/PASOEContent/static/auth/logout.jsp diff --git a/SportsApp/Sports/PASOEContent/static/commonPageFooter.html b/Sports/PASOEContent/static/commonPageFooter.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/commonPageFooter.html rename to Sports/PASOEContent/static/commonPageFooter.html diff --git a/SportsApp/Sports/PASOEContent/static/commonPageHeader.html b/Sports/PASOEContent/static/commonPageHeader.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/commonPageHeader.html rename to Sports/PASOEContent/static/commonPageHeader.html diff --git a/SportsApp/Sports/PASOEContent/static/commonStyle.css b/Sports/PASOEContent/static/commonStyle.css similarity index 100% rename from SportsApp/Sports/PASOEContent/static/commonStyle.css rename to Sports/PASOEContent/static/commonStyle.css diff --git a/SportsApp/Sports/PASOEContent/static/error/error401.html b/Sports/PASOEContent/static/error/error401.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/error/error401.html rename to Sports/PASOEContent/static/error/error401.html diff --git a/SportsApp/Sports/PASOEContent/static/error/error403.html b/Sports/PASOEContent/static/error/error403.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/error/error403.html rename to Sports/PASOEContent/static/error/error403.html diff --git a/SportsApp/Sports/PASOEContent/static/error/error404.html b/Sports/PASOEContent/static/error/error404.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/error/error404.html rename to Sports/PASOEContent/static/error/error404.html diff --git a/SportsApp/Sports/PASOEContent/static/error/error500.html b/Sports/PASOEContent/static/error/error500.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/error/error500.html rename to Sports/PASOEContent/static/error/error500.html diff --git a/SportsApp/Sports/PASOEContent/static/error/error503.html b/Sports/PASOEContent/static/error/error503.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/error/error503.html rename to Sports/PASOEContent/static/error/error503.html diff --git a/SportsApp/Sports/PASOEContent/static/home.html b/Sports/PASOEContent/static/home.html similarity index 100% rename from SportsApp/Sports/PASOEContent/static/home.html rename to Sports/PASOEContent/static/home.html diff --git a/SportsApp/Sports/PASOEContent/static/home.jsp b/Sports/PASOEContent/static/home.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/static/home.jsp rename to Sports/PASOEContent/static/home.jsp diff --git a/SportsApp/Sports/PASOEContent/static/images/Thumbs.db b/Sports/PASOEContent/static/images/Thumbs.db similarity index 100% rename from SportsApp/Sports/PASOEContent/static/images/Thumbs.db rename to Sports/PASOEContent/static/images/Thumbs.db diff --git a/SportsApp/Sports/PASOEContent/static/images/appserver.png b/Sports/PASOEContent/static/images/appserver.png similarity index 100% rename from SportsApp/Sports/PASOEContent/static/images/appserver.png rename to Sports/PASOEContent/static/images/appserver.png diff --git a/SportsApp/Sports/PASOEContent/static/images/appserver2.png b/Sports/PASOEContent/static/images/appserver2.png similarity index 100% rename from SportsApp/Sports/PASOEContent/static/images/appserver2.png rename to Sports/PASOEContent/static/images/appserver2.png diff --git a/SportsApp/Sports/PASOEContent/static/images/background.png b/Sports/PASOEContent/static/images/background.png similarity index 100% rename from SportsApp/Sports/PASOEContent/static/images/background.png rename to Sports/PASOEContent/static/images/background.png diff --git a/SportsApp/Sports/PASOEContent/static/images/communities.png b/Sports/PASOEContent/static/images/communities.png similarity index 100% rename from SportsApp/Sports/PASOEContent/static/images/communities.png rename to Sports/PASOEContent/static/images/communities.png diff --git a/SportsApp/Sports/PASOEContent/static/images/content.png b/Sports/PASOEContent/static/images/content.png similarity index 100% rename from SportsApp/Sports/PASOEContent/static/images/content.png rename to Sports/PASOEContent/static/images/content.png diff --git a/SportsApp/Sports/PASOEContent/static/images/documentation.png b/Sports/PASOEContent/static/images/documentation.png similarity index 100% rename from SportsApp/Sports/PASOEContent/static/images/documentation.png rename to Sports/PASOEContent/static/images/documentation.png diff --git a/SportsApp/Sports/PASOEContent/static/images/gear.png b/Sports/PASOEContent/static/images/gear.png similarity index 100% rename from SportsApp/Sports/PASOEContent/static/images/gear.png rename to Sports/PASOEContent/static/images/gear.png diff --git a/SportsApp/Sports/PASOEContent/static/images/progress.png b/Sports/PASOEContent/static/images/progress.png similarity index 100% rename from SportsApp/Sports/PASOEContent/static/images/progress.png rename to Sports/PASOEContent/static/images/progress.png diff --git a/SportsApp/Sports/PASOEContent/static/images/qstart.png b/Sports/PASOEContent/static/images/qstart.png similarity index 100% rename from SportsApp/Sports/PASOEContent/static/images/qstart.png rename to Sports/PASOEContent/static/images/qstart.png diff --git a/SportsApp/Sports/PASOEContent/static/images/videos.png b/Sports/PASOEContent/static/images/videos.png similarity index 100% rename from SportsApp/Sports/PASOEContent/static/images/videos.png rename to Sports/PASOEContent/static/images/videos.png diff --git a/SportsApp/Sports/PASOEContent/static/index.jsp b/Sports/PASOEContent/static/index.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/static/index.jsp rename to Sports/PASOEContent/static/index.jsp diff --git a/SportsApp/Sports/PASOEContent/static/serverstatus.js b/Sports/PASOEContent/static/serverstatus.js similarity index 100% rename from SportsApp/Sports/PASOEContent/static/serverstatus.js rename to Sports/PASOEContent/static/serverstatus.js diff --git a/SportsApp/Sports/PASOEContent/static/sessionsExceeded.jsp b/Sports/PASOEContent/static/sessionsExceeded.jsp similarity index 100% rename from SportsApp/Sports/PASOEContent/static/sessionsExceeded.jsp rename to Sports/PASOEContent/static/sessionsExceeded.jsp diff --git a/SportsApp/Sports/build.config b/Sports/build.config similarity index 99% rename from SportsApp/Sports/build.config rename to Sports/build.config index 4332e47..f734f74 100644 --- a/SportsApp/Sports/build.config +++ b/Sports/build.config @@ -120,7 +120,7 @@ avm { // ============================================================================================== // Boolean value to use '_progres' or 'prowin' executables. 'true' for _progres, 'false' for prowin. // ============================================================================================== - tty.enabled="false" + tty.enabled="true" // ============================================================================================== // String value to pass startup parameters. This will append to default params if diff --git a/SportsApp/Sports/build.gradle b/Sports/build.gradle similarity index 51% rename from SportsApp/Sports/build.gradle rename to Sports/build.gradle index 1eb78b9..e2a62a7 100644 --- a/SportsApp/Sports/build.gradle +++ b/Sports/build.gradle @@ -3,47 +3,83 @@ * * This is a general purpose Gradle build. * Learn more about Gradle by exploring our samples at https://docs.gradle.org/7.3.3/samples - * This project uses @Incubating APIs which are subject to change. */ -plugins { - id("progress.openedge.abl-base") version "2.2.1" - id("base") + +plugins { + id "progress.openedge.abl" version "2.2.1" } // Gather required variables -def stgEnv = System.getProperty("STAGE_ENVIRONMENT") -def STAGE_ENVIRONMENT = stgEnv != null ? stgEnv : System.getenv("STAGE_ENVIRONMENT") +def stageEnv = System.getProperty("STAGE_ENVIRONMENT") +stageEnv = stageEnv != null ? stageEnv : System.getenv("STAGE_ENVIRONMENT") +def STAGE_ENVIRONMENT = stageEnv != null ? stageEnv : "dev" println "DLC: ${abl.dlcHome}" println "Stage Environment: ${STAGE_ENVIRONMENT}" +group = 'com.progress.openedge' +version = "1.0.0" +description = 'Sports App' + +abl { + if (STAGE_ENVIRONMENT == "dev") { + dbConnection { + dbName="${buildDir}/db/sports2020/sports" + connectionParameters = "-1" + } + } else { + dbConnection { + parameterFile='conf/startup.pf' + } + } +} + +// ABL App tasks +task createSports2020(type: CreateDB){ + dbName = 'sports' + sourceDb = "${dlcHome}/sports2020" + outputDir = "${buildDir}/db/sports2020" +} +if (STAGE_ENVIRONMENT == "dev") { + compileAbl.dependsOn "createSports2020" +} + +tasks.named("compileAbl-root-AppServer"){ + rcodeDir = "${buildDir}/rcode/AppServer" +} -// WEB App tasks -task compileWebApp(type: ABLCompile){ - source "PASOEContent/WEB-INF/openedge" - propath "PASOEContent/WEB-INF/openedge" - include "**/*.cls" - include "**/*.p" +tasks.named("compileAbl-root-PASOEContent-WEB-INF-openedge"){ rcodeDir = "${buildDir}/rcode/PASOEContent/WEB-INF/openedge" } +tasks.named("compileAbl-root-tests-AppServer"){ + rcodeDir = "${buildDir}/rcode/tests/AppServer" +} + +task testABLApp(type: ABLUnit){ + source("tests/AppServer") + include("**/*Suite.cls") + propath("tests/AppServer", "AppServer") + outputDir = "${buildDir}/test-results/test" + arguments = [haltOnFailure: "true"] +} +if (STAGE_ENVIRONMENT == "dev") { + testABLApp.dependsOn "createSports2020" +} +check.dependsOn "testABLApp", "compileAbl" + + task packageWebApp(type: OEWar){ webAppName = "Sports" + archiveVersion = "" // to ignore version in the archive name, otherwise has to be handled during deployment + verbose = true projectLocation = project.projectDir println "projectLocation: ${projectLocation.get()}" - verbose = true - destinationDirectory = project.distsDirectory - - // exclude generated folders; - // because the ANT task used internally adds everything under the PASOEContent folder - // exclude "**/${buildDir.name}/**" - // exclude "**/output/**" + destinationDirectory = project.file "${project.distsDirectory.get()}/webapps" // exclude Sources from 'openedge' directory // (the ANT task used internally does it by default but added here anyway) - exclude "PASOEContent/WEB-INF/openedge/*.cls" - exclude "PASOEContent/WEB-INF/openedge/*.p" - exclude "PASOEContent/WEB-INF/openedge/*.i" + exclude "PASOEContent/WEB-INF/openedge/*.(cls|p|i)" webInf { from("${buildDir}/rcode/PASOEContent/WEB-INF/openedge") @@ -56,70 +92,20 @@ task packageWebApp(type: OEWar){ // from ("PASOEContent/META-INF/MANIFEST.MF") } } -packageWebApp.dependsOn "compileWebApp" - -// ABL App tasks -task createSports2020(type: CreateDB){ - dbName = 'sports' - sourceDb = "${dlcHome}/sports2020" - outputDir = "${buildDir}/db/sports2020" -} - -task compileABLApp(type: ABLCompile){ - source "AppServer" - propath "AppServer" - include "**/*.cls" - include "**/*.p" - rcodeDir = "${buildDir}/rcode/AppServer" - - if (STAGE_ENVIRONMENT == "prod" || STAGE_ENVIRONMENT == "staging") { - dbConnection { - parameterFile='conf/startup.pf' - } - } else { - dbConnection { - dbName="${buildDir}/db/sports2020/sports" - connectionParameters = "-1" - } - } - -} -if (STAGE_ENVIRONMENT != "prod" || STAGE_ENVIRONMENT == "staging") { - compileABLApp.dependsOn "createSports2020" -} - -task testABLApp(type: ABLUnit){ - source("tests") - include("**/*Suite.cls") - propath("tests", "AppServer") - outputDir = "${buildDir}/test-results/test" - arguments = [haltOnFailure: "true"] - - if (STAGE_ENVIRONMENT == "prod" || STAGE_ENVIRONMENT == "staging") { - dbConnection { - parameterFile='conf/startup.pf' - } - } else { - dbConnection { - dbName="${buildDir}/db/sports2020/sports" - connectionParameters = "-1" - } - } -} -if (STAGE_ENVIRONMENT != "prod" || STAGE_ENVIRONMENT == "staging") { - testABLApp.dependsOn "createSports2020" -} +packageWebApp.dependsOn "compileAbl-root-PASOEContent-WEB-INF-openedge" +assemble.dependsOn "packageWebApp" -task packageABLApp(type: Oear){ +task packageABLApp(type: Oear) { ablAppName = "Sports" - destinationDirectory = project.distsDirectory //will create 'Sports.oear' file at this location - tlr { + archiveVersion = "" + destinationDirectory = project.file "${project.distsDirectory.get()}/ablapps" //will create 'Sports.oear' file at this location + tlr { from 'tlr' include '**/*.properties' include '**/*.xml' } - webapps { - from project.distsDirectory + webapps { + from "${project.distsDirectory.get()}/webapps" include '**/*.war' include '**/*.zip' } @@ -137,8 +123,6 @@ task packageABLApp(type: Oear){ // from ("conf/MANIFEST.MF") } } -packageABLApp.dependsOn "compileABLApp" +packageABLApp.dependsOn "compileAbl-root-AppServer" packageABLApp.dependsOn "packageWebApp" - -assemble.dependsOn "packageABLApp" -build.dependsOn "testABLApp" \ No newline at end of file +assemble.dependsOn "packageABLApp" \ No newline at end of file diff --git a/SportsApp/Sports/conf/MANIFEST.MF b/Sports/conf/MANIFEST.MF similarity index 100% rename from SportsApp/Sports/conf/MANIFEST.MF rename to Sports/conf/MANIFEST.MF diff --git a/SportsApp/Sports/conf/startup.pf b/Sports/conf/startup.pf similarity index 100% rename from SportsApp/Sports/conf/startup.pf rename to Sports/conf/startup.pf diff --git a/SportsApp/deploy/Dockerfile b/Sports/docker/Dockerfile similarity index 92% rename from SportsApp/deploy/Dockerfile rename to Sports/docker/Dockerfile index c2410da..de4ffab 100644 --- a/SportsApp/deploy/Dockerfile +++ b/Sports/docker/Dockerfile @@ -2,14 +2,13 @@ FROM alpine:3.8 # Root location ARG ROOT_FOLDER=/deploy-staging - ARG MANIFEST_VERSION=1.0 # Copy archive file COPY ./ablapps/ ${ROOT_FOLDER}/artifacts/ablapps # Create "META-INF/MANIFEST.MF" file -RUN mkdir ${ROOT_FOLDER}/META-INF +RUN mkdir -p ${ROOT_FOLDER}/META-INF RUN echo "Manifest-Version: ${MANIFEST_VERSION}" > ${ROOT_FOLDER}/META-INF/MANIFEST.MF RUN echo "Date-Timestamp: `date +'%Y-%m-%dT%H:%M:%S%z'`" >> ${ROOT_FOLDER}/META-INF/MANIFEST.MF diff --git a/Sports2020Db/buildScript/license/.gitignore b/Sports/docker/ablapps/.gitignore similarity index 100% rename from Sports2020Db/buildScript/license/.gitignore rename to Sports/docker/ablapps/.gitignore diff --git a/Sports/docker/build.sh b/Sports/docker/build.sh new file mode 100644 index 0000000..c485ab7 --- /dev/null +++ b/Sports/docker/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "Building Docker Image" +docker build -t ${APP_NAME}:${APP_VERSION} . --no-cache +echo "Building Docker Image Complete!" \ No newline at end of file diff --git a/Sports/docker/test.sh b/Sports/docker/test.sh new file mode 100644 index 0000000..668a170 --- /dev/null +++ b/Sports/docker/test.sh @@ -0,0 +1,11 @@ +cd ./tests + +# downlad goss and dgoss +# export GOSS_DST=. +# export GOSS_VER=v0.3.16 +# curl -fsSL https://goss.rocks/install | sh + +# export GOSS_PATH=./goss +export GOSS_OPTS="--format junit" +echo "Running Goss Tests" +dgoss run -it ${APP_NAME}:${APP_VERSION} /bin/sh > unitTestReport.xml diff --git a/Sports/docker/tests/goss.yaml b/Sports/docker/tests/goss.yaml new file mode 100644 index 0000000..9651cc3 --- /dev/null +++ b/Sports/docker/tests/goss.yaml @@ -0,0 +1,13 @@ +file: + /deploy-staging: + exists: true + filetype: directory + contains: [] + /deploy-staging/META-INF/MANIFEST.MF: + exists: true + filetype: file + contains: [] + /deploy-staging/artifacts/ablapps/Sports.oear: + exists: true + filetype: file + contains: [] diff --git a/SportsApp/Sports/gradle/wrapper/gradle-wrapper.jar b/Sports/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from SportsApp/Sports/gradle/wrapper/gradle-wrapper.jar rename to Sports/gradle/wrapper/gradle-wrapper.jar diff --git a/SportsApp/Sports/gradle/wrapper/gradle-wrapper.properties b/Sports/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from SportsApp/Sports/gradle/wrapper/gradle-wrapper.properties rename to Sports/gradle/wrapper/gradle-wrapper.properties diff --git a/SportsApp/Sports/gradlew b/Sports/gradlew similarity index 95% rename from SportsApp/Sports/gradlew rename to Sports/gradlew index c53aefa..2f856aa 100644 --- a/SportsApp/Sports/gradlew +++ b/Sports/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright 2015-2021 the original authors. +# Copyright � 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,10 +32,10 @@ # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; -# * expansions $var, ${var}, ${var:-default}, ${var+SET}, -# ${var#prefix}, ${var%suffix}, and $( cmd ); -# * compound commands having a testable exit status, especially case; -# * various built-in commands including command, set, and ulimit. +# * expansions �$var�, �${var}�, �${var:-default}�, �${var+SET}�, +# �${var#prefix}�, �${var%suffix}�, and �$( cmd )�; +# * compound commands having a testable exit status, especially �case�; +# * various built-in commands including �command�, �set�, and �ulimit�. # # Important for patching: # diff --git a/SportsApp/Sports/gradlew.bat b/Sports/gradlew.bat similarity index 100% rename from SportsApp/Sports/gradlew.bat rename to Sports/gradlew.bat diff --git a/SportsApp/settings.gradle b/Sports/settings.gradle similarity index 86% rename from SportsApp/settings.gradle rename to Sports/settings.gradle index 16a4617..ea406c3 100644 --- a/SportsApp/settings.gradle +++ b/Sports/settings.gradle @@ -7,5 +7,4 @@ * in the user manual at https://docs.gradle.org/7.3.3/userguide/multi_project_builds.html */ -rootProject.name = 'OpenEdgeOps' -include "Sports" \ No newline at end of file +rootProject.name = 'Sports' diff --git a/SportsApp/Sports/tests/CustomerTest.cls b/Sports/tests/AppServer/CustomerTest.cls similarity index 100% rename from SportsApp/Sports/tests/CustomerTest.cls rename to Sports/tests/AppServer/CustomerTest.cls diff --git a/SportsApp/Sports/tests/OrderTest.cls b/Sports/tests/AppServer/OrderTest.cls similarity index 100% rename from SportsApp/Sports/tests/OrderTest.cls rename to Sports/tests/AppServer/OrderTest.cls diff --git a/SportsApp/Sports/tests/SportsTestSuite.cls b/Sports/tests/AppServer/SportsTestSuite.cls similarity index 100% rename from SportsApp/Sports/tests/SportsTestSuite.cls rename to Sports/tests/AppServer/SportsTestSuite.cls diff --git a/SportsApp/Sports/tlr/merge.properties b/Sports/tlr/merge.properties similarity index 97% rename from SportsApp/Sports/tlr/merge.properties rename to Sports/tlr/merge.properties index 3949d0b..3563f5c 100644 --- a/SportsApp/Sports/tlr/merge.properties +++ b/Sports/tlr/merge.properties @@ -5,7 +5,7 @@ [AppServer.Agent.Sports] numInitialSessions=2 PROPATH=${CATALINA_BASE}/webapps/Sports/WEB-INF/openedge,${CATALINA_BASE}/ablapps/Sports/openedge,${CATALINA_BASE}/openedge,${DLC}/tty,${DLC}/tty/netlib/OpenEdge.Net.apl - uuid=http://NBHYDRAHULK11:/Sports + uuid=http://${psc.as.host.name}:/Sports [AppServer.SessMgr.Sports] agentStartupParam=-T "${CATALINA_BASE}/temp" -pf ${CATALINA_BASE}/ablapps/Sports/conf/startup.pf diff --git a/SportsApp/Sports/tlr/openedge.properties.merge b/Sports/tlr/openedge.properties.merge similarity index 100% rename from SportsApp/Sports/tlr/openedge.properties.merge rename to Sports/tlr/openedge.properties.merge diff --git a/SportsApp/.gitignore b/SportsApp/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/SportsApp/Sports/.gitattributes b/SportsApp/Sports/.gitattributes deleted file mode 100644 index 00a51af..0000000 --- a/SportsApp/Sports/.gitattributes +++ /dev/null @@ -1,6 +0,0 @@ -# -# https://help.github.com/articles/dealing-with-line-endings/ -# -# These are explicitly windows files and should use crlf -*.bat text eol=crlf - diff --git a/SportsApp/Sports/.scripts/common-build-tasks.xml b/SportsApp/Sports/.scripts/common-build-tasks.xml deleted file mode 100644 index b716aaf..0000000 --- a/SportsApp/Sports/.scripts/common-build-tasks.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SportsApp/Sports/.scripts/deploy-app.sh b/SportsApp/Sports/.scripts/deploy-app.sh deleted file mode 100644 index d19f11d..0000000 --- a/SportsApp/Sports/.scripts/deploy-app.sh +++ /dev/null @@ -1,27 +0,0 @@ -set -e -export DLC=/psc/dlc -export PATH=$DLC/bin:$PATH -export PROJECT_NAME=$(echo `pwd` | rev | cut -f2 -d'/' - | rev); - -# Create PASOE instance -export DLC=/psc/dlc &&\ - cd /psc/wrk &&\ - /psc/dlc/bin/pasman create -v oepas1 - -# Replace all occurances of logs directory to volume mount -find ./ -type f -exec sed -i -e 's/${catalina.base}\/logs/\/var\/log/gI' {} \; - -# Copy application files - startup parameters - REST service -cp /app/${PROJECT_NAME}.props /psc/wrk -cp /app/${PROJECT_NAME}.war /psc/wrk - -# Copy ABL files to PASInstance - extracting from zip file -unzip /app/${PROJECT_NAME}.zip -d /psc/wrk/oepas1/openedge - -# Update startup parameters -cd /psc/wrk &&\ -/psc/wrk/oepas1/bin/oeprop.sh -v -f /psc/wrk/${PROJECT_NAME}.props - -# Deploy REST service -cd /psc/wrk &&\ -/psc/wrk/oepas1/bin/tcman.sh deploy -v /psc/wrk/${PROJECT_NAME}.war \ No newline at end of file diff --git a/SportsApp/Sports/.scripts/dotpropath-to-pctpropath.xsl b/SportsApp/Sports/.scripts/dotpropath-to-pctpropath.xsl deleted file mode 100644 index 956ac5d..0000000 --- a/SportsApp/Sports/.scripts/dotpropath-to-pctpropath.xsl +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SportsApp/Sports/.scripts/propath.xml b/SportsApp/Sports/.scripts/propath.xml deleted file mode 100644 index 72eff0e..0000000 --- a/SportsApp/Sports/.scripts/propath.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SportsApp/Sports/.scripts/standardpaths.xml b/SportsApp/Sports/.scripts/standardpaths.xml deleted file mode 100644 index 6acb72a..0000000 --- a/SportsApp/Sports/.scripts/standardpaths.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SportsApp/Sports/gradle.properties b/SportsApp/Sports/gradle.properties deleted file mode 100644 index a5e00c6..0000000 --- a/SportsApp/Sports/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -# DLC= -# STAGE_ENVIRONMENT= \ No newline at end of file diff --git a/SportsApp/Sports/settings.gradle b/SportsApp/Sports/settings.gradle deleted file mode 100644 index f70f4b7..0000000 --- a/SportsApp/Sports/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/7.3.3/userguide/multi_project_builds.html - * This project uses @Incubating APIs which are subject to change. - */ - -rootProject.name = 'Sports' \ No newline at end of file diff --git a/SportsApp/build.gradle b/SportsApp/build.gradle deleted file mode 100644 index 5811738..0000000 --- a/SportsApp/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * This is a general purpose Gradle build. - * Learn more about Gradle by exploring our samples at https://docs.gradle.org/7.3.3/samples - */ -plugins { - id("base") -} - -task packageDeploy(type: Tar) { - archiveFileName = "sportsApp.tar.gz" - destinationDirectory = project.distsDirectory - compression = Compression.GZIP - from ("deploy"){ - include "scripts/**" - include "*.sh" - include "*.yml" - include "Dockerfile" - exclude "license" - into "deploy" - } - from ("webui"){ - into "webui" - } - from ("Sports/build/distributions/Sports.oear"){ - into "deploy/ablapps" - } -} -packageDeploy.dependsOn ":Sports:build" -build.dependsOn packageDeploy \ No newline at end of file diff --git a/SportsApp/deploy/.gitignore b/SportsApp/deploy/.gitignore deleted file mode 100644 index 5629c95..0000000 --- a/SportsApp/deploy/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -output -webui \ No newline at end of file diff --git a/SportsApp/deploy/deploy.sh b/SportsApp/deploy/deploy.sh deleted file mode 100644 index 877c54f..0000000 --- a/SportsApp/deploy/deploy.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -# create the app docker image -docker build --no-cache -t sports:latest . - -# deploy -PAS_INSTANCE_NAME=oepas1 -docker-compose -p ${PAS_INSTANCE_NAME} up -d -echo "PASOE instance named '${PAS_INSTANCE_NAME}_dc' will be available at 'https://localhost:8811'" \ No newline at end of file diff --git a/SportsApp/deploy/undeploy.sh b/SportsApp/deploy/undeploy.sh deleted file mode 100644 index 4136638..0000000 --- a/SportsApp/deploy/undeploy.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -# undeploy -PAS_INSTANCE_NAME=oepas1 -docker-compose -p ${PAS_INSTANCE_NAME} down -v -echo "Removed '${PAS_INSTANCE_NAME}_dc'" \ No newline at end of file diff --git a/SportsApp/gradle/wrapper/gradle-wrapper.jar b/SportsApp/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL diff --git a/SportsApp/gradle/wrapper/gradle-wrapper.properties b/SportsApp/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 2e6e589..0000000 --- a/SportsApp/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/SportsApp/gradlew b/SportsApp/gradlew deleted file mode 100644 index c53aefa..0000000 --- a/SportsApp/gradlew +++ /dev/null @@ -1,234 +0,0 @@ -#!/bin/sh - -# -# Copyright 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions $var, ${var}, ${var:-default}, ${var+SET}, -# ${var#prefix}, ${var%suffix}, and $( cmd ); -# * compound commands having a testable exit status, especially case; -# * various built-in commands including command, set, and ulimit. -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/SportsApp/gradlew.bat b/SportsApp/gradlew.bat deleted file mode 100644 index 107acd3..0000000 --- a/SportsApp/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100644 index 0000000..61154ed --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# env vars +echo "DOCKER_REPO_URL=${DOCKER_REPO_URL}" +echo "OE_VERSION=${OE_VERSION}" +echo "APP_NAME=${APP_NAME}" +echo "APP_VERSION=${APP_VERSION}" +echo "APP_GROUP=${APP_GROUP}" + +# create the app docker image +# docker build --no-cache -t sports:latest . + +# deploy +PAS_INSTANCE_NAME=oepas1 +docker-compose --verbose -p ${PAS_INSTANCE_NAME} up -d diff --git a/SportsApp/deploy/docker-compose.yml b/deploy/docker-compose.yml similarity index 61% rename from SportsApp/deploy/docker-compose.yml rename to deploy/docker-compose.yml index 4e0375f..1eb71c7 100644 --- a/SportsApp/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -1,36 +1,42 @@ version: "3.6" services: oedbmachine: - image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-db:12.8.0 - ports: - - "7654:7654" - - "7664-7684:7664-7684" - environment: - - DB_BROKER_PORT=7654 - - DB_MINPORT=7664 - - DB_MAXPORT=7684 + image: ${DOCKER_REPO_URL}/openedge-db:${OE_VERSION} + ports: + - "7654:7654" + - "7664-7684:7664-7684" + environment: + - DB_BROKER_PORT=7654 + - DB_MINPORT=7664 + - DB_MAXPORT=7684 + webuiapp: + image: ${DOCKER_REPO_URL}/${APP_GROUP}/webui:latest + volumes: + - webui_dc:/webui web: - image: nginx - ports: - - "8080:80" - volumes: - - ./../webui:/usr/share/nginx/html:ro + image: nginx + depends_on: + - webuiapp + ports: + - "8080:80" + volumes: + - webui_dc:/usr/share/nginx/html:ro jdk: image: eclipse-temurin:17.0.3_7-jdk-centos7 volumes: - jdk_dc:/opt/java/openjdk ablapp: - image: sports:latest + image: ${DOCKER_REPO_URL}/${APP_GROUP}/${APP_NAME}:${APP_VERSION} volumes: - app_dc:/deploy-staging/artifacts pasoeinstance: - image: ec2-54-80-142-101.compute-1.amazonaws.com:9443/openedge-pasoe:12.8.0 + image: ${DOCKER_REPO_URL}/openedge-pasoe:${OE_VERSION} depends_on: - jdk - ablapp environment: - FLUENTBIT_LOGGING=true - - APP_NAME=sports + - APP_NAME=${APP_NAME} - INSTANCE_NAME=oepas1 ports: - "8811:8811" @@ -54,3 +60,4 @@ services: volumes: jdk_dc: app_dc: + webui_dc: diff --git a/SportsApp/deploy/ablapps/.gitignore b/deploy/license/.gitignore similarity index 100% rename from SportsApp/deploy/ablapps/.gitignore rename to deploy/license/.gitignore diff --git a/SportsApp/deploy/scripts/startServer.sh b/deploy/scripts/startServer.sh similarity index 100% rename from SportsApp/deploy/scripts/startServer.sh rename to deploy/scripts/startServer.sh diff --git a/deploy/undeploy.sh b/deploy/undeploy.sh new file mode 100644 index 0000000..ab46740 --- /dev/null +++ b/deploy/undeploy.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# env vars +echo "DOCKER_REPO_URL=${DOCKER_REPO_URL}" +echo "OE_VERSION=${OE_VERSION}" +echo "APP_NAME=${APP_NAME}" +echo "APP_VERSION=${APP_VERSION}" +echo "APP_GROUP=${APP_GROUP}" + +# undeploy +PAS_INSTANCE_NAME=oepas1 +docker-compose --verbose -p ${PAS_INSTANCE_NAME} down -v \ No newline at end of file diff --git a/devpas/build-image/.gitignore b/devpas/build-image/.gitignore new file mode 100644 index 0000000..86789cb --- /dev/null +++ b/devpas/build-image/.gitignore @@ -0,0 +1 @@ +installer \ No newline at end of file diff --git a/devpas/build-image/Dockerfile b/devpas/build-image/Dockerfile new file mode 100644 index 0000000..f7eb72d --- /dev/null +++ b/devpas/build-image/Dockerfile @@ -0,0 +1,45 @@ +ARG JDK_IMAGE=eclipse-temurin:17.0.3_7-jdk-centos7 + +FROM ${JDK_IMAGE} as builder-jdk + +FROM centos:7 as builder + +COPY installer installer + +COPY --from=builder-jdk /opt/java/openjdk /usr/java +COPY response.ini . + +RUN installer/proinst -b response.ini -l response.log + +RUN cat response.log +RUN echo "Docker Container Image for Dev PAS for OpenEdge 12.8.0 as of `date`" > /psc/dlc/image-version + +FROM centos:7 + +# Add a non root user +RUN groupadd pscadmin &&\ + useradd -g pscadmin pscadmin + +# Copy dlc, java and wrk +COPY --from=builder --chown=pscadmin:pscadmin /usr/java /usr/java +COPY --from=builder --chown=pscadmin:pscadmin /psc/dlc /psc/dlc +COPY --from=builder --chown=pscadmin:pscadmin /psc/wrk /psc/wrk + +# Copy init/run scripts +COPY scripts/* /psc/wrk/ + +# Restrict permissions +# Read and execute only +RUN chmod 0555 /psc/wrk/initcmd.sh + +# From now on run as abldojo user +USER pscadmin + +ENV JAVA_HOME=/usr/java \ + PATH=/psc/dlc:/psc/dlc/bin:/psc/dlc/ant/bin:$PATH \ + WRKDIR=/psc/wrk/ \ + DLC=/psc/dlc/ + +WORKDIR /psc/wrk/ + +CMD [ "/psc/wrk/initcmd.sh" ] \ No newline at end of file diff --git a/devpas/build-image/build.sh b/devpas/build-image/build.sh new file mode 100644 index 0000000..542cd3a --- /dev/null +++ b/devpas/build-image/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "Building Docker Image" +docker build -t openedge-dev-pasoe:12.8.0 . --no-cache +echo "Building Docker Image Complete!" \ No newline at end of file diff --git a/devpas/build-image/response.ini b/devpas/build-image/response.ini new file mode 100644 index 0000000..6eb19f7 --- /dev/null +++ b/devpas/build-image/response.ini @@ -0,0 +1,176 @@ +; +; DESCRIPTION of Configuration Count +; +; NumberofConfigurations - the number of products being installed. +; + +[Configuration Count] +NumberofConfigurations=4 + +[Product Configuration 1] +name=PROGRESS +serial= +version=12.8ALPHA +control= +prodname=4GL Development System + +[Product Configuration 2] +name=Progress Software +serial= +version=12.8ALPHA +control= +prodname=Client Networking + +[Product Configuration 3] +name=Progress Software +serial= +version=12.8ALPHA +control= +prodname=OE Adv. Ent. RDBMS + +[Product Configuration 4] +name=Progress Software +serial= +version=12.8ALPHA +control= +prodname=Progress Dev AS for OE + +; +; DESCRIPTION of OpenEdge Explorer +; +; enable - indicates whether or not you want to enable the OpenEdge Explorer functionality. +; - a value of false indicates you do NOT want to enable the OpenEdge Explorer functionality. +; - a value of true indicates you want to enable the OpenEdge Explorer functionality. +; + +[OpenEdge Explorer] +enable=true + +; +; DESCRIPTION of Java +; +; JavaHome - the root directory where the JRE is installed +; + +[Java] +JavaHome=/usr/java + +; +; DESCRIPTION of Type and Destination +; +; type - identifies the type of installation. Valid values are COMPLETE and CUSTOM. +; path - identifies the directory in which you install your OpenEdge product software. +; workpath - identifies the directory in which your applications, databases, and log files will reside. +; oem_path - identifies the directory in which you install your Management product software. +; oem_workpath - identifies the directory in which your Management applications, databases, and log files will reside. +; + +[Type and Destination] +type=COMPLETE +path=/psc/dlc +workpath=/psc/wrk +oem_path=/psc/oemgmt +oem_workpath=/psc/wrk_oemgmt + +; +; DESCRIPTION of Server Engine +; +; UseSqlServerEngine - valid values are 0 and 1. +; 0 - indicates that the SQL Database Engine is to not be installed. +; 1 - indicates that the SQL Database Engine is to be installed. +; + +[Server Engine] +UseSqlServerEngine=1 + +; +; DESCRIPTION of Language Default +; +; DefaultLanguage - identifies the language in which PROMSGS appears by default. +; -Valid values are: +; Czech +; Dutch +; English - American +; English - International +; French +; German +; Italian +; Polish +; Portuguese +; Portuguese - Brazilian +; Spanish +; Spanish - Latin +; Swedish +; + +[Language Default] +DefaultLanguage=English - International + +; +; DESCRIPTION of Language Choice +; +; lang1, lang2, lang3 ... - Identifies all the PROMSGS languages installed during installation including the default language. +; + +[Language Choice] +lang1=English - International + +; +; DESCRIPTION of International Settings +; +; NOTE: For specific information please refer to the intlsets.txt file located at the root level of the cdrom from which this information is derived. +; cpinternal - identifies the -cpinternal and -cpstream values included in the startup.pf file. +; cpcollation - identifies the -cpcoll value included in the startup.pf file. +; cpcase - identifies the -cpcase value included in the startup.pf file. +; dateformat - identifies the -d value included in the startup.pf file. +; numsep - identifies the -numsep value included in the startup.pf file. +; numdec - identifies the -numdec value included in the startup.pf file. +; +; The following is a table of the numbers and the separators they represent: +; 32 - space +; 36 - dollar +; 39 - apostrophe +; 44 - comma +; 46 - period +; + +[International Settings] +cpinternal=ISO8859-1 +cpcollation=Basic +cpcase=Basic +dateformat=mdy +numsep=44 +numdec=46 +; +; DESCRIPTION of PacificAppServerPortDetails +; +; nPortHttp - A port for HTTP connections. +; nPortHttps - A port for HTTPS connections. +; nPortShutdown - Port on which the shutdown process will run. +; + +[PacificAppServerPortDetails] +nPortHttp=8810 +nPortHttps=8811 +nPortShutdown=8812 + +; +; DESCRIPTION of STS Key Plugin Dialog +; +; EnableAuthGatewaySTSClient - Enable Authentication Gateway. Enable = 1, Disable = 0 +; OEAuthGatewayURL - Gateway URL. +; ServerKeyPassword - Password +; STSKeystorePath- Path for storing keys. +; NoHostVerify - Enable = 1, Disable = 0 +; EnableAdminSrvSTSKeyPlugin - Enable = 1, Disable = 0 +; PollingInterVal - Polling interval (in minutes). +; + +[STS Key Plugin Dialog] +EnableAuthGatewaySTSClient=0 +OEAuthGatewayURL= +ServerKeyPassword= +STSKeystorePath= +NoHostVerify=0 +EnableAdminSrvSTSKeyPlugin=0 +PollingInterVal= diff --git a/devpas/build-image/scripts/initcmd.sh b/devpas/build-image/scripts/initcmd.sh new file mode 100644 index 0000000..1b278eb --- /dev/null +++ b/devpas/build-image/scripts/initcmd.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cat /psc/dlc/image-version +echo "APP_LOCATION: ${APP_LOCATION}" + +echo "Building app at ${APP_LOCATION}!" +cd ${APP_LOCATION} +sh gradlew clean build +echo "Building app at ${APP_LOCATION} completed!" diff --git a/Sports2020Db/buildScript/Dockerfile b/sports-db/build-script/Dockerfile similarity index 100% rename from Sports2020Db/buildScript/Dockerfile rename to sports-db/build-script/Dockerfile diff --git a/Sports2020Db/buildScript/build.sh b/sports-db/build-script/build.sh similarity index 100% rename from Sports2020Db/buildScript/build.sh rename to sports-db/build-script/build.sh diff --git a/Sports2020Db/buildScript/config.properties b/sports-db/build-script/config.properties similarity index 100% rename from Sports2020Db/buildScript/config.properties rename to sports-db/build-script/config.properties diff --git a/Sports2020Db/buildScript/hook-script.sh b/sports-db/build-script/hook-script.sh similarity index 100% rename from Sports2020Db/buildScript/hook-script.sh rename to sports-db/build-script/hook-script.sh diff --git a/SportsApp/deploy/license/.gitignore b/sports-db/build-script/license/.gitignore similarity index 100% rename from SportsApp/deploy/license/.gitignore rename to sports-db/build-script/license/.gitignore diff --git a/Sports2020Db/buildScript/validate.sh b/sports-db/build-script/validate.sh similarity index 100% rename from Sports2020Db/buildScript/validate.sh rename to sports-db/build-script/validate.sh diff --git a/test-app-image/package.json b/test-app-image/package.json new file mode 100644 index 0000000..0217ad8 --- /dev/null +++ b/test-app-image/package.json @@ -0,0 +1,21 @@ +{ + "name": "pasoe-basic", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "mocha --recursive", + "test:rest": "mocha --recursive test/test*.js --reporter mocha-junit-reporter" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "chai": "^4.1.2", + "mocha": "^5.2.0", + "mocha-junit-reporter": "^1.23.3" + }, + "dependencies": { + "@progress/jsdo-node": "^5.0.0-2018-05-11-00002" + } +} diff --git a/test-app-image/test-app-image.sh b/test-app-image/test-app-image.sh new file mode 100644 index 0000000..c08e0c2 --- /dev/null +++ b/test-app-image/test-app-image.sh @@ -0,0 +1,14 @@ +echo "Starting test of Services of the Sample Sports App!" + +# Run Test using NodeJS +export PATH=$PATH:/home/ubuntu/.nvm/versions/node/v6.9.1/bin/ +npm install + +node --version +npm --version + +echo "Waiting for the application to come up." +sleep 45s +npm run test:rest + +echo "Starting test of Services of the Sample Sports App Complete!" \ No newline at end of file diff --git a/test-app-image/test/testCustomer.js b/test-app-image/test/testCustomer.js new file mode 100644 index 0000000..445cc83 --- /dev/null +++ b/test-app-image/test/testCustomer.js @@ -0,0 +1,176 @@ +process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; + +const chai = require("chai"); +const assert = chai.assert; +const expect = chai.expect; +const progress = require("@progress/jsdo-core").progress; + + +chai.should(); + +var jsdo; +var result, + newCustNum, + balance; + +describe("Test Customer", () => { + const options = { + serviceURI: "https://ec2-18-206-124-116.compute-1.amazonaws.com:8811/Sports", + catalogURI: "https://ec2-18-206-124-116.compute-1.amazonaws.com:8811/Sports/static/SportsService.json", + resourceName: "Customer", + authenticationModel: "anonymous" + }; + + before(function (done) { + progress.data.getSession(options).then((object) => { + jsdo = new progress.data.JSDO({ + name: options.resourceName + }); + }, (error) => { + throw new error("Error creating session"); + }).then(() => done(), () => done()); + }); + describe("Create Customer", () => { + before(function (done) { + jsdo.add({ + Name: "Test Customer", + SalesRep: "BBB", + Balance: 10000 + }); + jsdo.saveChanges(true).then((object) => { + result = object; + // console.log("DEBUG: SUCCESS: Result: ", result.request.jsrecords[0].data); + newCustNum = result.request.jsrecords[0].data.CustNum; + }, (object) => { + result = object; + // console.log("DEBUG: ERROR: Result: ", result); + }).then(() => done(), () => done()); + }); + + it("should return an object", () => { + result.should.be.an("object"); + }); + it("should return success", () => { + result.success.should.equals(true); + }); + it("should return a CustNum greater than zero", () => { + newCustNum.should.be.at.least(1); + }); + it("new record should be returned by read() operation", (done) => { + jsdo.read({ + filter: { + field: "CustNum", + operator: "eq", + value: newCustNum + } + }).then(() => { + // console.log("DEBUG: SUCCESS: "); + if (jsdo.getData().length === 1) { + done(); + } else { + done(new Error("Record not found")); + } + }, () => { + // console.log("DEBUG: ERROR: "); + done(new Error("Error reading records")); + }); + }); + }); + describe("Read Customer", () => { + before(function (done) { + jsdo.read().then((object) => { + result = object + }, (object) => { + result = object + }).then(() => done(), () => done()); + }); + + it("should return an object", () => { + result.should.be.an("object"); + }); + it("should return a number of records greater than zero", () => { + jsdo.getData().length.should.be.at.least(1); + }); + }); + describe("Update Customer", () => { + before(function (done) { + var jsrecord = jsdo.find((record) => { + return record.data.CustNum === newCustNum; + }); + jsrecord.assign({ Balance: 12345 }); + jsdo.saveChanges(true).then((object) => { + result = object; + balance = result.request.jsrecords[0].data.Balance; + }, (object) => { + result = object; + }).then(() => done(), () => done()); + }); + it("should return an object", () => { + result.should.be.an("object"); + }); + it("should return success", () => { + result.success.should.equals(true); + }); + it("should return a Balance equals to 12345", () => { + balance.should.equals(12345); + }); + it("Updated record is returned by read() operation", (done) => { + jsdo.read({ + filter: { + field: "CustNum", + operator: "eq", + value: newCustNum + } + }).then(() => { + // console.log("DEBUG: SUCCESS: "); + if (jsdo.getData().length === 1 + && jsdo.getData()[0].Balance === 12345) { + done(); + } else { + done(new Error("Matching record was not found")); + } + }, () => { + // console.log("DEBUG: ERROR: "); + done(new Error("Error reading records")); + }); + }); + }); + describe("Delete Customer", () => { + before(function (done) { + var jsrecord = jsdo.find((record) => { + return record.data.CustNum === newCustNum; + }); + jsrecord.remove(); + jsdo.saveChanges(true).then((object) => { + result = object; + }, (object) => { + result = object; + }).then(() => done(), () => done()); + }); + it("should return an object", () => { + result.should.be.an("object"); + }); + it("should return success", () => { + result.success.should.equals(true); + }); + it("deleted record should not be returned by read() operation", (done) => { + jsdo.read({ + filter: { + field: "CustNum", + operator: "eq", + value: newCustNum + } + }).then(() => { + // console.log("DEBUG: SUCCESS: "); + if (jsdo.getData().length === 1) { + done(new Error("Record was found")); + } else { + done(); + } + }, () => { + // console.log("DEBUG: ERROR: "); + done(new Error("Error reading records")); + }); + }); + }); +}); diff --git a/webui/Dockerfile b/webui/Dockerfile new file mode 100644 index 0000000..5069b7b --- /dev/null +++ b/webui/Dockerfile @@ -0,0 +1,17 @@ +FROM alpine:3.8 + +# Root location +ARG ROOT_FOLDER=/webui +ARG MANIFEST_VERSION=1.0 + +# Create "META-INF/MANIFEST.MF" file +RUN mkdir -p ${ROOT_FOLDER}/META-INF +RUN echo "Manifest-Version: ${MANIFEST_VERSION}" > ${ROOT_FOLDER}/META-INF/MANIFEST.MF +RUN echo "Date-Timestamp: `date +'%Y-%m-%dT%H:%M:%S%z'`" >> ${ROOT_FOLDER}/META-INF/MANIFEST.MF + +# Copy web app files +COPY ./src/ ${ROOT_FOLDER}/ + +# Set working directory +WORKDIR ${ROOT_FOLDER} + diff --git a/webui/build.sh b/webui/build.sh new file mode 100644 index 0000000..fdd51df --- /dev/null +++ b/webui/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "Building Docker Image" +docker build -t webui:latest . --no-cache +echo "Building Docker Image complete!" \ No newline at end of file diff --git a/SportsApp/webui/grid.js b/webui/src/grid.js similarity index 96% rename from SportsApp/webui/grid.js rename to webui/src/grid.js index e6c9df0..8871a6d 100644 --- a/SportsApp/webui/grid.js +++ b/webui/src/grid.js @@ -4,7 +4,7 @@ $(function () { 'use strict'; - var serviceURI = "https://ec2-54-161-12-110.compute-1.amazonaws.com:8811/Sports"; + var serviceURI = "https://ec2-18-206-124-116.compute-1.amazonaws.com:8811/Sports"; var catalogURI = serviceURI + "/static/SportsService.json"; function createGrid() { diff --git a/SportsApp/webui/index.html b/webui/src/index.html similarity index 96% rename from SportsApp/webui/index.html rename to webui/src/index.html index 7eee979..ef21b4b 100644 --- a/SportsApp/webui/index.html +++ b/webui/src/index.html @@ -44,7 +44,7 @@

diff --git a/SportsApp/webui/progress.all.js b/webui/src/progress.all.js similarity index 100% rename from SportsApp/webui/progress.all.js rename to webui/src/progress.all.js diff --git a/webui/test.sh b/webui/test.sh new file mode 100644 index 0000000..81da9dc --- /dev/null +++ b/webui/test.sh @@ -0,0 +1,11 @@ +cd ./tests + +# downlad goss and dgoss +# export GOSS_DST=. +# export GOSS_VER=v0.3.16 +# curl -fsSL https://goss.rocks/install | sh + +# export GOSS_PATH=./goss +export GOSS_OPTS="--format junit" +echo "Running Goss Tests" +dgoss run -it webui:latest /bin/sh > unitTestReport.xml \ No newline at end of file diff --git a/webui/tests/goss.yaml b/webui/tests/goss.yaml new file mode 100644 index 0000000..7be8578 --- /dev/null +++ b/webui/tests/goss.yaml @@ -0,0 +1,21 @@ +file: + /webui: + exists: true + filetype: directory + contains: [] + /webui/META-INF/MANIFEST.MF: + exists: true + filetype: file + contains: [] + /webui/grid.js: + exists: true + filetype: file + contains: [] + /webui/index.html: + exists: true + filetype: file + contains: [] + /webui/progress.all.js: + exists: true + filetype: file + contains: [] From f1234e80279dc168e7efb6495a953ad30f45c769 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 21 Sep 2023 03:20:14 +0530 Subject: [PATCH 126/135] limit readcustomer --- Sports/AppServer/Customer.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sports/AppServer/Customer.cls b/Sports/AppServer/Customer.cls index 692df17..6ca2149 100644 --- a/Sports/AppServer/Customer.cls +++ b/Sports/AppServer/Customer.cls @@ -170,7 +170,7 @@ CLASS Customer INHERITS BusinessEntity: END. iCount = 0. - REPEAT WHILE NOT hQuery:QUERY-OFF-END AND iCount < iMaxRows: + REPEAT WHILE NOT hQuery:QUERY-OFF-END AND iCount < 5: hQuery:GET-NEXT () NO-ERROR. IF AVAILABLE Customer THEN DO: From 05f78b9c185caa84750498386290e02ae52fd4f1 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 21 Sep 2023 03:45:44 +0530 Subject: [PATCH 127/135] test change --- Sports/AppServer/Customer.cls | 4 ++-- test-app-image/test/testCustomer.js | 4 ++-- webui/src/grid.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sports/AppServer/Customer.cls b/Sports/AppServer/Customer.cls index 6ca2149..3aa7329 100644 --- a/Sports/AppServer/Customer.cls +++ b/Sports/AppServer/Customer.cls @@ -113,7 +113,7 @@ CLASS Customer INHERITS BusinessEntity: jsonParser = NEW ObjectModelParser(). jsonObject = CAST(jsonParser:Parse(filter), jsonObject). - iMaxRows = jsonObject:GetInteger("top") NO-ERROR. + iMaxRows = 5. iSkipRows = jsonObject:GetInteger("skip") NO-ERROR. ablFilter = jsonObject:GetCharacter("ablFilter") NO-ERROR. id = jsonObject:GetCharacter("id") NO-ERROR. @@ -170,7 +170,7 @@ CLASS Customer INHERITS BusinessEntity: END. iCount = 0. - REPEAT WHILE NOT hQuery:QUERY-OFF-END AND iCount < 5: + REPEAT WHILE NOT hQuery:QUERY-OFF-END AND iCount < iMaxRows: hQuery:GET-NEXT () NO-ERROR. IF AVAILABLE Customer THEN DO: diff --git a/test-app-image/test/testCustomer.js b/test-app-image/test/testCustomer.js index 445cc83..18b0013 100644 --- a/test-app-image/test/testCustomer.js +++ b/test-app-image/test/testCustomer.js @@ -15,8 +15,8 @@ var result, describe("Test Customer", () => { const options = { - serviceURI: "https://ec2-18-206-124-116.compute-1.amazonaws.com:8811/Sports", - catalogURI: "https://ec2-18-206-124-116.compute-1.amazonaws.com:8811/Sports/static/SportsService.json", + serviceURI: "https://ec2-3-84-2-121.compute-1.amazonaws.com/:8811/Sports", + catalogURI: "https://ec2-3-84-2-121.compute-1.amazonaws.com:8811/Sports/static/SportsService.json", resourceName: "Customer", authenticationModel: "anonymous" }; diff --git a/webui/src/grid.js b/webui/src/grid.js index 8871a6d..9783932 100644 --- a/webui/src/grid.js +++ b/webui/src/grid.js @@ -4,7 +4,7 @@ $(function () { 'use strict'; - var serviceURI = "https://ec2-18-206-124-116.compute-1.amazonaws.com:8811/Sports"; + var serviceURI = "https://ec2-3-84-2-121.compute-1.amazonaws.com:8811/Sports"; var catalogURI = serviceURI + "/static/SportsService.json"; function createGrid() { From 72233a8a725108a76236128ad845c64ccbf21899 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 21 Sep 2023 03:56:55 +0530 Subject: [PATCH 128/135] test --- Sports/AppServer/Customer.cls | 100 ---------------------------------- deploy/deploy.sh | 2 +- deploy/undeploy.sh | 2 +- 3 files changed, 2 insertions(+), 102 deletions(-) diff --git a/Sports/AppServer/Customer.cls b/Sports/AppServer/Customer.cls index 3aa7329..fa05ad3 100644 --- a/Sports/AppServer/Customer.cls +++ b/Sports/AppServer/Customer.cls @@ -95,106 +95,6 @@ CLASS Customer INHERITS BusinessEntity: METHOD PRIVATE VOID JFPFillMethod(INPUT filter AS CHARACTER): - - DEFINE VARIABLE jsonParser AS ObjectModelParser NO-UNDO. - DEFINE VARIABLE jsonObject AS JsonObject NO-UNDO. - DEFINE VARIABLE cWhere AS CHARACTER NO-UNDO. - DEFINE VARIABLE hQuery AS HANDLE NO-UNDO. - DEFINE VARIABLE lUseReposition AS LOGICAL NO-UNDO. - DEFINE VARIABLE iCount AS INTEGER NO-UNDO. - DEFINE VARIABLE ablFilter AS CHARACTER NO-UNDO. - DEFINE VARIABLE id AS CHARACTER INITIAL ? NO-UNDO. - DEFINE VARIABLE iMaxRows AS INTEGER INITIAL ? NO-UNDO. - DEFINE VARIABLE iSkipRows AS INTEGER INITIAL ? NO-UNDO. - DEFINE VARIABLE cOrderBy AS CHARACTER INITIAL "" NO-UNDO. - - /* purge any existing data */ - EMPTY TEMP-TABLE ttCustomer. - - jsonParser = NEW ObjectModelParser(). - jsonObject = CAST(jsonParser:Parse(filter), jsonObject). - iMaxRows = 5. - iSkipRows = jsonObject:GetInteger("skip") NO-ERROR. - ablFilter = jsonObject:GetCharacter("ablFilter") NO-ERROR. - id = jsonObject:GetCharacter("id") NO-ERROR. - cOrderBy = jsonObject:GetCharacter("orderBy") NO-ERROR. - cWhere = "WHERE " + ablFilter NO-ERROR. - - IF cOrderBy > "" THEN - DO: - cOrderBy = REPLACE(cOrderBy, ",", " by "). - cOrderBy = "by " + cOrderBy + " ". - /* NOTE: id and seq fields should be removed from - cWhere and cOrderBy */ - cOrderBy = REPLACE(cOrderBy, "by id desc", ""). - cOrderBy = REPLACE(cOrderBy, "by id ", ""). - cOrderBy = REPLACE(cOrderBy, "by seq desc", ""). - cOrderBy = REPLACE(cOrderBy, "by seq ", ""). - END. - - lUseReposition = iSkipRows <> ?. - - IF iMaxRows <> ? AND iMaxRows > 0 THEN - DO: - BUFFER ttCustomer:HANDLE:BATCH-SIZE = iMaxRows. - END. - ELSE - DO: - IF id > "" THEN - BUFFER ttCustomer:HANDLE:BATCH-SIZE = 1. - ELSE - BUFFER ttCustomer:HANDLE:BATCH-SIZE = 0. - END. - - BUFFER ttCustomer:ATTACH-DATA-SOURCE(DATA-SOURCE srcCustomer:HANDLE). - - IF cOrderBy = ? THEN cOrderBy = "". - cWhere = IF cWhere > "" THEN (cWhere + " " + cOrderBy) - ELSE ("WHERE " + cOrderBy). - DATA-SOURCE srcCustomer:FILL-WHERE-STRING = cWhere. - - IF lUseReposition THEN - DO: - hQuery = DATA-SOURCE srcCustomer:QUERY. - hQuery:QUERY-OPEN. - - IF id > "" AND id <> "?" THEN - DO: - hQuery:REPOSITION-TO-ROWID(TO-ROWID(id)). - END. - ELSE IF iSkipRows <> ? AND iSkipRows > 0 THEN - DO: - hQuery:REPOSITION-TO-ROW(iSkipRows). - IF NOT AVAILABLE Customer THEN - hQuery:GET-NEXT() NO-ERROR. - END. - - iCount = 0. - REPEAT WHILE NOT hQuery:QUERY-OFF-END AND iCount < iMaxRows: - hQuery:GET-NEXT () NO-ERROR. - IF AVAILABLE Customer THEN - DO: - CREATE ttCustomer. - BUFFER-COPY Customer TO ttCustomer. - ASSIGN - ttCustomer.id = STRING(ROWID(Customer)) - iSeq = iSeq + 1 - ttCustomer.seq = iSeq. - END. - iCount = iCount + 1. - END. - END. - ELSE - DO: - IF id > "" THEN DATA-SOURCE srcCustomer:RESTART-ROWID(1) - = TO-ROWID ((id)). - BUFFER ttCustomer:SET-CALLBACK ("AFTER-ROW-FILL", "AddIdField"). - DATASET dsCustomer:FILL(). - END. - - FINALLY: - BUFFER ttCustomer:DETACH-DATA-SOURCE(). - END FINALLY. END METHOD. diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 61154ed..43c914b 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -12,4 +12,4 @@ echo "APP_GROUP=${APP_GROUP}" # deploy PAS_INSTANCE_NAME=oepas1 -docker-compose --verbose -p ${PAS_INSTANCE_NAME} up -d +docker-compose -p ${PAS_INSTANCE_NAME} up -d diff --git a/deploy/undeploy.sh b/deploy/undeploy.sh index ab46740..141b8a4 100644 --- a/deploy/undeploy.sh +++ b/deploy/undeploy.sh @@ -9,4 +9,4 @@ echo "APP_GROUP=${APP_GROUP}" # undeploy PAS_INSTANCE_NAME=oepas1 -docker-compose --verbose -p ${PAS_INSTANCE_NAME} down -v \ No newline at end of file +docker-compose -p ${PAS_INSTANCE_NAME} down -v \ No newline at end of file From 997a3851e3d24afaf501c269fda5670232cbe6f4 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 21 Sep 2023 04:16:15 +0530 Subject: [PATCH 129/135] test --- Sports/AppServer/Customer.cls | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Sports/AppServer/Customer.cls b/Sports/AppServer/Customer.cls index fa05ad3..2561597 100644 --- a/Sports/AppServer/Customer.cls +++ b/Sports/AppServer/Customer.cls @@ -48,14 +48,7 @@ CLASS Customer INHERITS BusinessEntity: INPUT filter AS CHARACTER, OUTPUT DATASET dsCustomer): - IF filter BEGINS "~{" THEN - THIS-OBJECT:JFPFillMethod (INPUT filter). - ELSE - DO: - BUFFER ttCustomer:HANDLE:BATCH-SIZE = 0. - BUFFER ttCustomer:SET-CALLBACK ("AFTER-ROW-FILL", "AddIdField"). - SUPER:ReadData(filter). - END. + END METHOD. /* Other CUD and Submit operation methods */ From 9d48094491d8d39d70a758648e102d5d01469677 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 21 Sep 2023 04:29:24 +0530 Subject: [PATCH 130/135] test --- .github/workflows/development.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 8426d5a..4a3bd0f 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -128,6 +128,7 @@ jobs: with: files: ${{ github.workspace }}/test-app-image/*.xml check_name: Test Results - Sports App Services + action_fail: true - name: Undeploy the app and clean up resources run: echo "TODO" securityscans: From af9c6190b1ad79d0dd134d5cb216eb83e0dabdb3 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 21 Sep 2023 04:38:22 +0530 Subject: [PATCH 131/135] test --- Sports/AppServer/Customer.cls | 115 ++++++++++++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 4 deletions(-) diff --git a/Sports/AppServer/Customer.cls b/Sports/AppServer/Customer.cls index 2561597..7e42c25 100644 --- a/Sports/AppServer/Customer.cls +++ b/Sports/AppServer/Customer.cls @@ -48,7 +48,14 @@ CLASS Customer INHERITS BusinessEntity: INPUT filter AS CHARACTER, OUTPUT DATASET dsCustomer): - + IF filter BEGINS "~{" THEN + THIS-OBJECT:JFPFillMethod (INPUT filter). + ELSE + DO: + BUFFER ttCustomer:HANDLE:BATCH-SIZE = 0. + BUFFER ttCustomer:SET-CALLBACK ("AFTER-ROW-FILL", "AddIdField"). + SUPER:ReadData(filter). + END. END METHOD. /* Other CUD and Submit operation methods */ @@ -88,6 +95,106 @@ CLASS Customer INHERITS BusinessEntity: METHOD PRIVATE VOID JFPFillMethod(INPUT filter AS CHARACTER): + + DEFINE VARIABLE jsonParser AS ObjectModelParser NO-UNDO. + DEFINE VARIABLE jsonObject AS JsonObject NO-UNDO. + DEFINE VARIABLE cWhere AS CHARACTER NO-UNDO. + DEFINE VARIABLE hQuery AS HANDLE NO-UNDO. + DEFINE VARIABLE lUseReposition AS LOGICAL NO-UNDO. + DEFINE VARIABLE iCount AS INTEGER NO-UNDO. + DEFINE VARIABLE ablFilter AS CHARACTER NO-UNDO. + DEFINE VARIABLE id AS CHARACTER INITIAL ? NO-UNDO. + DEFINE VARIABLE iMaxRows AS INTEGER INITIAL ? NO-UNDO. + DEFINE VARIABLE iSkipRows AS INTEGER INITIAL ? NO-UNDO. + DEFINE VARIABLE cOrderBy AS CHARACTER INITIAL "" NO-UNDO. + + /* purge any existing data */ + EMPTY TEMP-TABLE ttCustomer. + + jsonParser = NEW ObjectModelParser(). + jsonObject = CAST(jsonParser:Parse(filter), jsonObject). + iMaxRows = jsonObject:GetInteger("top") NO-ERROR. + iSkipRows = jsonObject:GetInteger("skip") NO-ERROR. + ablFilter = jsonObject:GetCharacter("ablFilter") NO-ERROR. + id = jsonObject:GetCharacter("id") NO-ERROR. + cOrderBy = jsonObject:GetCharacter("orderBy") NO-ERROR. + cWhere = "WHERE " + ablFilter NO-ERROR. + + IF cOrderBy > "" THEN + DO: + cOrderBy = REPLACE(cOrderBy, ",", " by "). + cOrderBy = "by " + cOrderBy + " ". + /* NOTE: id and seq fields should be removed from + cWhere and cOrderBy */ + cOrderBy = REPLACE(cOrderBy, "by id desc", ""). + cOrderBy = REPLACE(cOrderBy, "by id ", ""). + cOrderBy = REPLACE(cOrderBy, "by seq desc", ""). + cOrderBy = REPLACE(cOrderBy, "by seq ", ""). + END. + + lUseReposition = iSkipRows <> ?. + + IF iMaxRows <> ? AND iMaxRows > 0 THEN + DO: + BUFFER ttCustomer:HANDLE:BATCH-SIZE = iMaxRows. + END. + ELSE + DO: + IF id > "" THEN + BUFFER ttCustomer:HANDLE:BATCH-SIZE = 1. + ELSE + BUFFER ttCustomer:HANDLE:BATCH-SIZE = 0. + END. + + BUFFER ttCustomer:ATTACH-DATA-SOURCE(DATA-SOURCE srcCustomer:HANDLE). + + IF cOrderBy = ? THEN cOrderBy = "". + cWhere = IF cWhere > "" THEN (cWhere + " " + cOrderBy) + ELSE ("WHERE " + cOrderBy). + DATA-SOURCE srcCustomer:FILL-WHERE-STRING = cWhere. + + IF lUseReposition THEN + DO: + hQuery = DATA-SOURCE srcCustomer:QUERY. + hQuery:QUERY-OPEN. + + IF id > "" AND id <> "?" THEN + DO: + hQuery:REPOSITION-TO-ROWID(TO-ROWID(id)). + END. + ELSE IF iSkipRows <> ? AND iSkipRows > 0 THEN + DO: + hQuery:REPOSITION-TO-ROW(iSkipRows). + IF NOT AVAILABLE Customer THEN + hQuery:GET-NEXT() NO-ERROR. + END. + + iCount = 0. + REPEAT WHILE NOT hQuery:QUERY-OFF-END AND iCount < iMaxRows: + hQuery:GET-NEXT () NO-ERROR. + IF AVAILABLE Customer THEN + DO: + CREATE ttCustomer. + BUFFER-COPY Customer TO ttCustomer. + ASSIGN + ttCustomer.id = STRING(ROWID(Customer)) + iSeq = iSeq + 1 + ttCustomer.seq = iSeq. + END. + iCount = iCount + 1. + END. + END. + ELSE + DO: + IF id > "" THEN DATA-SOURCE srcCustomer:RESTART-ROWID(1) + = TO-ROWID ((id)). + BUFFER ttCustomer:SET-CALLBACK ("AFTER-ROW-FILL", "AddIdField"). + DATASET dsCustomer:FILL(). + END. + + FINALLY: + BUFFER ttCustomer:DETACH-DATA-SOURCE(). + END FINALLY. END METHOD. @@ -101,7 +208,7 @@ CLASS Customer INHERITS BusinessEntity: @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="false"). @progress.service.resourceMapping(type="REST", operation="invoke", URI="/count?filter=~{filter~}", alias="", mediaType="application/json"). METHOD PUBLIC VOID count( INPUT filter AS CHARACTER, OUTPUT numRecs AS INTEGER): - DEFINE VARIABLE jsonParser AS ObjectModelParser NO-UNDO. + DEFINE VARIABLE jParser AS ObjectModelParser NO-UNDO. DEFINE VARIABLE jsonObject AS JsonObject NO-UNDO. DEFINE VARIABLE ablFilter AS CHARACTER NO-UNDO. DEFINE VARIABLE cWhere AS CHARACTER NO-UNDO. @@ -111,8 +218,8 @@ CLASS Customer INHERITS BusinessEntity: cWhere = filter. ELSE IF filter BEGINS "~{" THEN DO: - jsonParser = NEW ObjectModelParser(). - jsonObject = CAST(jsonParser:Parse(filter), jsonObject). + jParser = NEW ObjectModelParser(). + jsonObject = CAST(jParser:Parse(filter), jsonObject). ablFilter = jsonObject:GetCharacter("ablFilter") NO-ERROR. cWhere = "WHERE " + ablFilter. END. From 5634e76831953a743a21715a837b4ee51a1d3563 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 21 Sep 2023 04:46:39 +0530 Subject: [PATCH 132/135] test --- test-app-image/test/testCustomer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-app-image/test/testCustomer.js b/test-app-image/test/testCustomer.js index 18b0013..97f9558 100644 --- a/test-app-image/test/testCustomer.js +++ b/test-app-image/test/testCustomer.js @@ -15,7 +15,7 @@ var result, describe("Test Customer", () => { const options = { - serviceURI: "https://ec2-3-84-2-121.compute-1.amazonaws.com/:8811/Sports", + serviceURI: "https://ec2-3-84-2-121.compute-1.amazonaws.com:8811/Sports", catalogURI: "https://ec2-3-84-2-121.compute-1.amazonaws.com:8811/Sports/static/SportsService.json", resourceName: "Customer", authenticationModel: "anonymous" From ed1c121ff99130775d4bf44aeb8bb2324d2cd0b0 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Thu, 21 Sep 2023 05:43:01 -0400 Subject: [PATCH 133/135] Add Sigrid steps --- .github/workflows/sigrid-publish.yml | 18 ++++++++++++++++++ .github/workflows/sigrid-pullrequest.yml | 15 +++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 .github/workflows/sigrid-publish.yml create mode 100644 .github/workflows/sigrid-pullrequest.yml diff --git a/.github/workflows/sigrid-publish.yml b/.github/workflows/sigrid-publish.yml new file mode 100644 index 0000000..4ebabdc --- /dev/null +++ b/.github/workflows/sigrid-publish.yml @@ -0,0 +1,18 @@ +name: sigrid-publish +on: + push: + branches: + - "main" + +jobs: + sigridci: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v3 + - name: Download Sigrid CI + run: "git clone https://github.com/Software-Improvement-Group/sigridci.git sigridci" + - name: "Run Sigrid CI" + env: + SIGRID_CI_TOKEN: "${{ secrets.SIGRID_CI_TOKEN }}" + run: "./sigridci/sigridci/sigridci.py --customer Progress Software --system Sample App --source . --publish" \ No newline at end of file diff --git a/.github/workflows/sigrid-pullrequest.yml b/.github/workflows/sigrid-pullrequest.yml new file mode 100644 index 0000000..1a2b40a --- /dev/null +++ b/.github/workflows/sigrid-pullrequest.yml @@ -0,0 +1,15 @@ +name: sigrid-pullrequest +on: [pull_request] + +jobs: + sigridci: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v3 + - name: Download Sigrid CI + run: "git clone https://github.com/Software-Improvement-Group/sigridci.git sigridci" + - name: "Run Sigrid CI" + env: + SIGRID_CI_TOKEN: "${{ secrets.SIGRID_CI_TOKEN }}" + run: "./sigridci/sigridci/sigridci.py --customer Progress Software --system Sample App --source ." \ No newline at end of file From 1d5e783dd178076d57e2c391375f42b5fd73c075 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Thu, 21 Sep 2023 06:25:33 -0400 Subject: [PATCH 134/135] updates the sigrid and undelete of workflow --- .github/workflows/development.yml | 173 +++++++++++++++++++++++ .github/workflows/sigrid-publish.yml | 2 +- .github/workflows/sigrid-pullrequest.yml | 2 +- 3 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/development.yml diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml new file mode 100644 index 0000000..4773643 --- /dev/null +++ b/.github/workflows/development.yml @@ -0,0 +1,173 @@ +name: Development +run-name: ${{ github.actor }} is compiling our Sample App 🚀 +on: [push] +jobs: + compile: + name: OpenEdge Compile Job + permissions: write-all + runs-on: self-hosted + defaults: + run: + working-directory: ${{ github.workspace }}/Sports + steps: + - uses: actions/checkout@v3 + - name: Pull Dev PASOE Docker Image + run: | + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker pull ${{ vars.DOCKER_REPO_URL }}/openedge-dev-pasoe:${{ vars.OE_VERSION }} + - name: Running Gradle build in a Container + run: docker run --rm -v ./:/psc/wrk/Sports/ -e APP_LOCATION=/psc/wrk/Sports ${{ vars.DOCKER_REPO_URL }}/openedge-dev-pasoe:${{ vars.OE_VERSION }} + - name: Setup Python - needed by publish step + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Publish ABL Unit Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + if: always() + with: + files: ${{ github.workspace }}/Sports/build/test-results/test/*.xml + check_name: Test Results - ABL Unit tests for Sports App + build: + name: Build Docker Image for Sports App + needs: compile + permissions: write-all + runs-on: self-hosted + defaults: + run: + working-directory: ${{ github.workspace }}/Sports/docker + steps: + - name: Copy ABLApp archive ('.oear') + run: cp -rf ./../build/distributions/ablapps/*.oear ./ablapps + - name: Docker build + run: sh build.sh + env: + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} + - name: Test Docker Image state - Goss + run: sh test.sh + env: + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} + - name: Setup Python - needed by publish step + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Publish Goss Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + with: + files: ${{ github.workspace }}/Sports/docker/tests/*.xml + check_name: Test Results - Goss tests for Sports App + - name: Push Docker Image to Develop Docker Registry + run: | + docker tag ${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} + buildwebui: + name: Build Docker Image for Web UI App + permissions: write-all + runs-on: self-hosted + defaults: + run: + working-directory: ${{ github.workspace }}/webui + steps: + - name: Docker build + run: sh build.sh + - name: Test Docker Image state - Goss + run: sh test.sh + - name: Setup Python - needed by publish step + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Publish Goss Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + with: + files: ${{ github.workspace }}/webui/tests/*.xml + check_name: Test Results - Goss tests for Web UI App + - name: Push Docker Image to Develop Docker Registry + run: | + docker tag webui:latest ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest + deploy: + name: Test Sample App Deploy + needs: [build, buildwebui] + permissions: write-all + runs-on: self-hosted + defaults: + run: + working-directory: ${{ github.workspace }}/deploy + steps: + - run: mkdir -p ./license + - name: Download the OpenEdge License file + run: wget -cO - ${{vars.NEXUS_URL}}/OpenEdge/linux/${{vars.OE_VERSION}}/linux-${{vars.OE_VERSION}}-license.cfg > ./license/progress.cfg --no-check-certificate + - name: Undeploy previous version of Sample App + run: sh undeploy.sh + env: + DOCKER_REPO_URL: ${{ vars.DOCKER_REPO_URL }} + OE_VERSION: ${{ vars.OE_VERSION }} + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} + APP_GROUP: ${{ vars.APP_GROUP }} + - name: Deploy new version of Sample App + run: sh deploy.sh + env: + DOCKER_REPO_URL: ${{ vars.DOCKER_REPO_URL }} + OE_VERSION: ${{ vars.OE_VERSION }} + APP_NAME: ${{ vars.APP_NAME }} + APP_VERSION: ${{ vars.APP_VERSION }} + APP_GROUP: ${{ vars.APP_GROUP }} + - name: Test the app + run: sh test-app-image.sh + working-directory: ${{ github.workspace }}/test-app-image + - name: Setup Python - needed by publish step + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Publish Sample Sports App Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + with: + files: ${{ github.workspace }}/test-app-image/*.xml + check_name: Test Results - Sports App Services + action_fail: true + - name: Undeploy the app and clean up resources + run: echo "TODO" + securityscans: + name: Scan Docker Images for Security Vulnerabilities + needs: [build, buildwebui] + permissions: write-all + runs-on: self-hosted + steps: + - name: Run security scan for Sports App docker image + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} + format: 'sarif' + output: 'trivy-results-sports.sarif' + - name: Upload security scan report of Sports App docker image to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: 'trivy-results-sports.sarif' + category: sports-app-scan + - name: Run security scan for Web UI App docker image + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest + format: 'sarif' + output: 'trivy-results-webui.sarif' + - name: Upload security scan report of Web UI App docker image to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: 'trivy-results-webui.sarif' + category: webui-app-scan + stage: + name: Stage Artifacts and Docker Images for Release + needs: [deploy, securityscans] + runs-on: self-hosted + steps: + - name: Publish Docker Images to Release Docker Registry + run: | + docker login ${{ vars.DOCKER_REPO_URL }} -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PWD }} + docker tag ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest-tested + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/webui:latest-tested + docker tag ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }} ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }}-tested + docker push ${{ vars.DOCKER_REPO_URL }}/${{ vars.APP_GROUP }}/${{ vars.APP_NAME }}:${{ vars.APP_VERSION }}-tested \ No newline at end of file diff --git a/.github/workflows/sigrid-publish.yml b/.github/workflows/sigrid-publish.yml index 4ebabdc..b6fa18a 100644 --- a/.github/workflows/sigrid-publish.yml +++ b/.github/workflows/sigrid-publish.yml @@ -15,4 +15,4 @@ jobs: - name: "Run Sigrid CI" env: SIGRID_CI_TOKEN: "${{ secrets.SIGRID_CI_TOKEN }}" - run: "./sigridci/sigridci/sigridci.py --customer Progress Software --system Sample App --source . --publish" \ No newline at end of file + run: "./sigridci/sigridci/sigridci.py --customer 'Progress Software' --system 'Sample App' --source . --publish" \ No newline at end of file diff --git a/.github/workflows/sigrid-pullrequest.yml b/.github/workflows/sigrid-pullrequest.yml index 1a2b40a..9c7c1ba 100644 --- a/.github/workflows/sigrid-pullrequest.yml +++ b/.github/workflows/sigrid-pullrequest.yml @@ -12,4 +12,4 @@ jobs: - name: "Run Sigrid CI" env: SIGRID_CI_TOKEN: "${{ secrets.SIGRID_CI_TOKEN }}" - run: "./sigridci/sigridci/sigridci.py --customer Progress Software --system Sample App --source ." \ No newline at end of file + run: "./sigridci/sigridci/sigridci.py --customer 'Progress Software' --system 'Sample App' --source ." \ No newline at end of file From 75878afca9a54ab6871570168ace6fc8c5140080 Mon Sep 17 00:00:00 2001 From: Cameron David Wright Date: Thu, 21 Sep 2023 06:27:18 -0400 Subject: [PATCH 135/135] updated pull request --- .github/workflows/sigrid-pullrequest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sigrid-pullrequest.yml b/.github/workflows/sigrid-pullrequest.yml index 9c7c1ba..1cbe2d9 100644 --- a/.github/workflows/sigrid-pullrequest.yml +++ b/.github/workflows/sigrid-pullrequest.yml @@ -12,4 +12,4 @@ jobs: - name: "Run Sigrid CI" env: SIGRID_CI_TOKEN: "${{ secrets.SIGRID_CI_TOKEN }}" - run: "./sigridci/sigridci/sigridci.py --customer 'Progress Software' --system 'Sample App' --source ." \ No newline at end of file + run: "./sigridci/sigridci/sigridci.py --customer 'Progress Software' --system 'SampleApp' --source ." \ No newline at end of file