diff --git a/.git-hooks/commit-msg b/.git-hooks/commit-msg
new file mode 100755
index 0000000..e0297de
--- /dev/null
+++ b/.git-hooks/commit-msg
@@ -0,0 +1,18 @@
+#!/bin/sh
+RED="\033[1;31m"
+GREEN="\033[1;32m"
+
+# Get the commit message (the parameter we're given is just the path to the
+# temporary file which holds the message).
+commit_message=$(cat "$1")
+
+if (echo "$commit_message" | grep -Eq "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-z \-]+\))?!?: .+$") then
+ echo "${GREEN} ✔ Commit message meets Conventional Commit standards"
+ exit 0
+fi
+
+echo "${RED}❌ Commit message does not meet the Conventional Commit standard!"
+echo "An example of a valid message is:"
+echo " feat(login): add the 'remember me' button"
+echo "ℹ More details at: https://www.conventionalcommits.org/en/v1.0.0/#summary"
+exit 1
\ No newline at end of file
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..ddbab98
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,4 @@
+sasjs-tests/
+docs/
+.github/
+CONTRIBUTING.md
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7cd6756..7680af8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Change Log
+Since March 2020 the changelog is managed by github releases - see [https://github.com/sasjs/adapter/releases](https://github.com/sasjs/adapter/releases).
+
+## Changes up to 5th March 2020
+
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index c93e457..83067b1 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -2,75 +2,127 @@
## Our Pledge
-In the interest of fostering an open and welcoming environment, we as
-contributors and maintainers pledge to making participation in our project and
-our community a harassment-free experience for everyone, regardless of age, body
-size, disability, ethnicity, sex characteristics, gender identity and expression,
-level of experience, education, socio-economic status, nationality, personal
-appearance, race, religion, or sexual identity and orientation.
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
## Our Standards
-Examples of behavior that contributes to creating a positive environment
-include:
+Examples of behavior that contributes to a positive environment for our
+community include:
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+ overall community
-Examples of unacceptable behavior by participants include:
+Examples of unacceptable behavior include:
-* The use of sexualized language or imagery and unwelcome sexual attention or
- advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
+* The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
-* Publishing others' private information, such as a physical or electronic
- address, without explicit permission
+* Publishing others' private information, such as a physical or email
+ address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
- professional setting
+ professional setting
-## Our Responsibilities
+## Enforcement Responsibilities
-Project maintainers are responsible for clarifying the standards of acceptable
-behavior and are expected to take appropriate and fair corrective action in
-response to any instances of unacceptable behavior.
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct, or to ban temporarily or
-permanently any contributor for other behaviors that they deem inappropriate,
-threatening, offensive, or harmful.
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
## Scope
-This Code of Conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community. Examples of
-representing a project or community include using an official project e-mail
-address, posting via an official social media account, or acting as an appointed
-representative at an online or offline event. Representation of a project may be
-further defined and clarified by project maintainers.
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at support@macropeople.com. All
-complaints will be reviewed and investigated and will result in a response that
-is deemed necessary and appropriate to the circumstances. The project team is
-obligated to maintain confidentiality with regard to the reporter of an incident.
-Further details of specific enforcement policies may be posted separately.
+reported to the community leaders responsible for enforcement at
+https://sasapps.io/contact-us.
+All complaints will be reviewed and investigated promptly and fairly.
-Project maintainers who do not follow or enforce the Code of Conduct in good
-faith may face temporary or permanent repercussions as determined by other
-members of the project's leadership.
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
## Attribution
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
-available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
-For answers to common questions about this code of conduct, see
-https://www.contributor-covenant.org/faq
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
diff --git a/LICENSE b/LICENSE
index 6765f8e..0f743d6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2020 Macro People
+Copyright (c) 2021 Macro People
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 9833c9c..9d267fd 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,23 @@
-[](https://www.jsdelivr.com/package/npm/@sasjs/adapter)
-
# @sasjs/adapter
+[![npm package][npm-image]][npm-url]
+[![Github Workflow][githubworkflow-image]][githubworkflow-url]
+[![Dependency Status][dependency-image]][dependency-url]
+[]()
+
+[](/LICENSE)
+
+
+[](https://gitpod.io/#https://github.com/sasjs/adapter)
+
+
+[npm-image]:https://img.shields.io/npm/v/@sasjs/adapter.svg
+[npm-url]:http://npmjs.org/package/@sasjs/adapter
+[githubworkflow-image]:https://github.com/sasjs/adapter/actions/workflows/build.yml/badge.svg
+[githubworkflow-url]:https://github.com/sasjs/adapter/blob/main/.github/workflows/build.yml
+[dependency-image]:https://david-dm.org/sasjs/adapter.svg
+[dependency-url]:https://github.com/sasjs/adapter/blob/main/package.json
+
SASjs is a open-source framework for building Web Apps on SAS® platforms. You can use as much or as little of it as you like. This repository contains the JS adapter, the part that handles the to/from SAS communication on the client side. There are 3 ways to install it:
1 - `npm install @sasjs/adapter` - for use in a node project
@@ -198,8 +214,15 @@ This approach is by far the fastest, as a result of the optimisations we have bu
# More resources
-For more information and examples specific to this adapter you can check out the [user guide](https://sasjs.io/sasjs-adapter/) or the [technical](http://adapter.sasjs.io/) documentation.
+For more information and examples specific to this adapter you can check out the [user guide](https://sasjs.io/sasjs-adapter/) or the [technical](http://adapter.sasjs.io/) documentation.
For more information on building web apps in general, check out these [resources](https://sasjs.io/training/resources/) or contact the [author](https://www.linkedin.com/in/allanbowe/) directly.
If you are a SAS 9 or SAS Viya customer you can also request a copy of [Data Controller](https://datacontroller.io) - free for up to 5 users, this tool makes use of all parts of the SASjs framework.
+
+
+## Star Gazing
+
+If you find this library useful, help us grow our star graph!
+
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 83cbe72..abd0f9f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,6 +5,7 @@
"packages": {
"": {
"name": "@sasjs/adapter",
+ "hasInstallScript": true,
"license": "ISC",
"dependencies": {
"@sasjs/utils": "^2.10.2",
diff --git a/package.json b/package.json
index f144baa..6754eff 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,7 @@
{
"name": "@sasjs/adapter",
"description": "JavaScript adapter for SAS",
+ "homepage": "https://adapter.sasjs.io",
"scripts": {
"build": "rimraf build && rimraf node && mkdir node && cp -r src/* node && webpack && rimraf build/src && rimraf node",
"package:lib": "npm run build && cp ./package.json build && cd build && npm version \"5.0.0\" && npm pack",
@@ -11,7 +12,8 @@
"prepublishOnly": "cp -r ./build/* . && rm -rf ./build",
"postpublish": "git clean -fd",
"semantic-release": "semantic-release",
- "typedoc": "typedoc"
+ "typedoc": "typedoc",
+ "postinstall": "[ -d .git ] && git config core.hooksPath ./.git-hooks || true"
},
"publishConfig": {
"access": "public"
diff --git a/sasjs-tests/README.md b/sasjs-tests/README.md
index b2698ba..4a7f87b 100644
--- a/sasjs-tests/README.md
+++ b/sasjs-tests/README.md
@@ -55,6 +55,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
filename ft15f001 temp;
parmcards4;
+ %webout(FETCH)
%webout(OPEN)
%macro x();
%do i=1 %to &_webin_file_count; %webout(OBJ,&&_webin_name&i) %end;
@@ -63,6 +64,7 @@ parmcards4;
;;;;
%mm_createwebservice(path=/Public/app/common,name=sendObj)
parmcards4;
+ %webout(FETCH)
%webout(OPEN)
%macro x();
%do i=1 %to &_webin_file_count; %webout(ARR,&&_webin_name&i) %end;
diff --git a/src/SASViyaApiClient.ts b/src/SASViyaApiClient.ts
index 8f177ad..72e9fa6 100644
--- a/src/SASViyaApiClient.ts
+++ b/src/SASViyaApiClient.ts
@@ -1078,6 +1078,7 @@ export class SASViyaApiClient {
) {
let POLL_INTERVAL = 300
let MAX_POLL_COUNT = 1000
+ let MAX_ERROR_COUNT = 5
if (pollOptions) {
POLL_INTERVAL = pollOptions.POLL_INTERVAL || POLL_INTERVAL
@@ -1086,6 +1087,7 @@ export class SASViyaApiClient {
let postedJobState = ''
let pollCount = 0
+ let errorCount = 0
const headers: any = {
'Content-Type': 'application/json',
'If-None-Match': etag
@@ -1100,14 +1102,18 @@ export class SASViyaApiClient {
const { result: state } = await this.requestClient
.get(
- `${this.serverUrl}${stateLink.href}?_action=wait&wait=30`,
+ `${this.serverUrl}${stateLink.href}?_action=wait&wait=300`,
accessToken,
'text/plain',
{},
this.debug
)
.catch((err) => {
- throw prefixMessage(err, 'Error while getting job state. ')
+ console.error(
+ `Error fetching job state from ${this.serverUrl}${stateLink.href}. Starting poll, assuming job to be running.`,
+ err
+ )
+ return { result: 'unavailable' }
})
const currentState = state.trim()
@@ -1122,25 +1128,40 @@ export class SASViyaApiClient {
if (
postedJobState === 'running' ||
postedJobState === '' ||
- postedJobState === 'pending'
+ postedJobState === 'pending' ||
+ postedJobState === 'unavailable'
) {
if (stateLink) {
const { result: jobState } = await this.requestClient
.get(
- `${this.serverUrl}${stateLink.href}?_action=wait&wait=30`,
+ `${this.serverUrl}${stateLink.href}?_action=wait&wait=300`,
accessToken,
'text/plain',
{},
this.debug
)
.catch((err) => {
- throw prefixMessage(
- err,
- 'Error while getting job state after interval. '
+ errorCount++
+ if (
+ pollCount >= MAX_POLL_COUNT ||
+ errorCount >= MAX_ERROR_COUNT
+ ) {
+ throw prefixMessage(
+ err,
+ 'Error while getting job state after interval. '
+ )
+ }
+ console.error(
+ `Error fetching job state from ${this.serverUrl}${stateLink.href}. Resuming poll, assuming job to be running.`,
+ err
)
+ return { result: 'unavailable' }
})
postedJobState = jobState.trim()
+ if (postedJobState != 'unavailable' && errorCount > 0) {
+ errorCount = 0
+ }
if (this.debug && printedState !== postedJobState) {
console.log('Polling job status...')
diff --git a/src/job-execution/JesJobExecutor.ts b/src/job-execution/JesJobExecutor.ts
index 60cf52f..42d22a4 100644
--- a/src/job-execution/JesJobExecutor.ts
+++ b/src/job-execution/JesJobExecutor.ts
@@ -33,7 +33,7 @@ export class JesJobExecutor extends BaseJobExecutor {
.then((response) => {
this.appendRequest(response, sasJob, config.debug)
- resolve(response.result)
+ resolve(response)
})
.catch(async (e: Error) => {
if (e instanceof JobExecutionError) {
diff --git a/src/job-execution/Sas9JobExecutor.ts b/src/job-execution/Sas9JobExecutor.ts
index f69f8aa..2c0cae3 100644
--- a/src/job-execution/Sas9JobExecutor.ts
+++ b/src/job-execution/Sas9JobExecutor.ts
@@ -5,6 +5,12 @@ import { convertToCSV, isRelativePath } from '../utils'
import { BaseJobExecutor } from './JobExecutor'
import { Sas9RequestClient } from '../request/Sas9RequestClient'
+/**
+ * Job executor for SAS9 servers for use in Node.js environments.
+ * Initiates login with the provided username and password from the config
+ * The cookies are stored in the request client and used in subsequent
+ * job execution requests.
+ */
export class Sas9JobExecutor extends BaseJobExecutor {
private requestClient: Sas9RequestClient
constructor(
diff --git a/src/request/Sas9RequestClient.ts b/src/request/Sas9RequestClient.ts
index 7f0b9f3..4d88d57 100644
--- a/src/request/Sas9RequestClient.ts
+++ b/src/request/Sas9RequestClient.ts
@@ -4,6 +4,10 @@ import * as tough from 'tough-cookie'
import { prefixMessage } from '@sasjs/utils/error'
import { RequestClient, throwIfError } from './RequestClient'
+/**
+ * Specific request client for SAS9 in Node.js environments.
+ * Handles redirects and cookie management.
+ */
export class Sas9RequestClient extends RequestClient {
constructor(baseUrl: string, allowInsecure = false) {
super(baseUrl, allowInsecure)