diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d2639fd..71870a7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -37,10 +37,32 @@ updates: time: "08:00" - package-ecosystem: "nuget" - directory: "/client-samples/dotnet/rest" + directory: "/client-samples/dotnet/rest/api-template" commit-message: prefix: "nuget-rest" schedule: interval: "monthly" day: "monday" time: "08:00" + + - package-ecosystem: "nuget" + directory: "/client-samples/dotnet/rest/api-template.tests" + commit-message: + prefix: "nuget-rest-tests" + schedule: + interval: "monthly" + day: "monday" + time: "08:00" + + - package-ecosystem: "github-actions" + directory: "/" + commit-message: + prefix: "github-actions" + schedule: + interval: "monthly" + day: "monday" + time: "08:00" + groups: + actions: + patterns: + - "actions/*" diff --git a/.github/workflows/dotnet-ci.yml b/.github/workflows/dotnet-rest-ci.yml similarity index 69% rename from .github/workflows/dotnet-ci.yml rename to .github/workflows/dotnet-rest-ci.yml index 92ba618..61191cf 100644 --- a/.github/workflows/dotnet-ci.yml +++ b/.github/workflows/dotnet-rest-ci.yml @@ -1,25 +1,27 @@ -name: .NET CI +name: .NET REST Tests on: push: branches: [ "main" ] paths: - - 'client-samples/dotnet/**' + - 'client-samples/dotnet/rest/**' + - '.github/workflows/dotnet-rest-ci.yml' pull_request: branches: [ "main" ] paths: - - 'client-samples/dotnet/**' + - 'client-samples/dotnet/rest/**' + - '.github/workflows/dotnet-rest-ci.yml' jobs: - dotnet-checkout-and-test: + dotnet-rest-ci: runs-on: windows-latest defaults: run: working-directory: ./client-samples/dotnet/rest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v5 with: dotnet-version: 9.0.x - name: Restore dependencies diff --git a/.github/workflows/java-base-ci.yml b/.github/workflows/java-base-ci.yml new file mode 100644 index 0000000..317ee75 --- /dev/null +++ b/.github/workflows/java-base-ci.yml @@ -0,0 +1,33 @@ +name: Reusable Java CI Workflow + +on: + workflow_call: + inputs: + working-directory: + required: true + type: string + description: 'Path to the directory containing the Java code to test' + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + java-version: ['11', '17', '21'] + defaults: + run: + working-directory: ${{ inputs.working-directory }} + steps: + - name: Checkout repository + uses: actions/checkout@v5 + - name: Set up JDK + uses: actions/setup-java@v5 + with: + java-version: ${{ matrix.java-version }} + distribution: 'zulu' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + - name: Gradle Build + run: ./gradlew build + - name: Gradle Test + run: ./gradlew test diff --git a/.github/workflows/java-ci.yml b/.github/workflows/java-ci.yml deleted file mode 100644 index 6a866f7..0000000 --- a/.github/workflows/java-ci.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Java CI - -on: - push: - branches: [ "main" ] - paths: - - 'client-samples/java/**' - pull_request: - branches: [ "main" ] - paths: - - 'client-samples/java/**' -jobs: - java-checkout-and-test: - runs-on: ubuntu-latest - strategy: - matrix: - java-version: ['11', '17', '21'] - working-directory: - - ./client-samples/java/rest - - ./client-samples/java/websockets - defaults: - run: - working-directory: ${{ matrix.working-directory }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Set up JDK - uses: actions/setup-java@v4 - with: - java-version: ${{ matrix.java-version }} - distribution: 'zulu' - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - name: Gradle Build - run: ./gradlew build - - name: Gradle Test - run: ./gradlew test diff --git a/.github/workflows/java-rest-ci.yml b/.github/workflows/java-rest-ci.yml new file mode 100644 index 0000000..1a1f831 --- /dev/null +++ b/.github/workflows/java-rest-ci.yml @@ -0,0 +1,22 @@ +name: Java REST Tests + +on: + push: + branches: [ "main" ] + paths: + - 'client-samples/java/rest/**' + - '.github/workflows/java-rest-ci.yml' + - '.github/workflows/java-base-ci.yml' + pull_request: + branches: [ "main" ] + paths: + - 'client-samples/java/rest/**' + - '.github/workflows/java-rest-ci.yml' + - '.github/workflows/java-base-ci.yml' + +jobs: + # REST tests + rest-ci: + uses: ./.github/workflows/java-base-ci.yml + with: + working-directory: ./client-samples/java/rest diff --git a/.github/workflows/java-websockets-ci.yml b/.github/workflows/java-websockets-ci.yml new file mode 100644 index 0000000..13773bc --- /dev/null +++ b/.github/workflows/java-websockets-ci.yml @@ -0,0 +1,22 @@ +name: Java WebSockets Tests + +on: + push: + branches: [ "main" ] + paths: + - 'client-samples/java/websockets/**' + - '.github/workflows/java-websockets-ci.yml' + - '.github/workflows/java-base-ci.yml' + pull_request: + branches: [ "main" ] + paths: + - 'client-samples/java/websockets/**' + - '.github/workflows/java-websockets-ci.yml' + - '.github/workflows/java-base-ci.yml' + +jobs: + # WebSockets tests + websockets-ci: + uses: ./.github/workflows/java-base-ci.yml + with: + working-directory: ./client-samples/java/websockets diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-base-ci.yml similarity index 50% rename from .github/workflows/python-ci.yml rename to .github/workflows/python-base-ci.yml index 0871faf..adc8d02 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-base-ci.yml @@ -1,31 +1,27 @@ -name: Python Tests +name: Reusable Python CI Workflow on: - push: - branches: [ "main" ] - paths: - - "client-samples/python/**" - pull_request: - branches: [ "main" ] - paths: - - "client-samples/python/**" + workflow_call: + inputs: + working-directory: + required: true + type: string + description: 'Path to the directory containing the Python code to test' + jobs: - python-checkout-and-test: + test: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] - working-directory: - - ./client-samples/python/rest - - ./client-samples/python/websockets + python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] defaults: run: - working-directory: ${{ matrix.working-directory }} + working-directory: ${{ inputs.working-directory }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Upgrade pip @@ -37,4 +33,4 @@ jobs: - name: Run pytest run: pytest tests/ - name: Run black check (linting) - run: black --check . \ No newline at end of file + run: black --check . diff --git a/.github/workflows/python-rest-ci.yml b/.github/workflows/python-rest-ci.yml new file mode 100644 index 0000000..2581d6d --- /dev/null +++ b/.github/workflows/python-rest-ci.yml @@ -0,0 +1,22 @@ +name: Python REST Tests + +on: + push: + branches: [ "main" ] + paths: + - "client-samples/python/rest/**" + - ".github/workflows/python-rest-ci.yml" + - ".github/workflows/python-base-ci.yml" + pull_request: + branches: [ "main" ] + paths: + - "client-samples/python/rest/**" + - ".github/workflows/python-rest-ci.yml" + - ".github/workflows/python-base-ci.yml" + +jobs: + # REST tests + rest-ci: + uses: ./.github/workflows/python-base-ci.yml + with: + working-directory: ./client-samples/python/rest diff --git a/.github/workflows/python-websockets-ci.yml b/.github/workflows/python-websockets-ci.yml new file mode 100644 index 0000000..f65fbdf --- /dev/null +++ b/.github/workflows/python-websockets-ci.yml @@ -0,0 +1,22 @@ +name: Python WebSockets Tests + +on: + push: + branches: [ "main" ] + paths: + - "client-samples/python/websockets/**" + - ".github/workflows/python-websockets-ci.yml" + - ".github/workflows/python-base-ci.yml" + pull_request: + branches: [ "main" ] + paths: + - "client-samples/python/websockets/**" + - ".github/workflows/python-websockets-ci.yml" + - ".github/workflows/python-base-ci.yml" + +jobs: + # WebSockets tests + websockets-ci: + uses: ./.github/workflows/python-base-ci.yml + with: + working-directory: ./client-samples/python/websockets diff --git a/client-samples/python/rest/README.md b/client-samples/python/rest/README.md index 8ec4358..ba8fef3 100644 --- a/client-samples/python/rest/README.md +++ b/client-samples/python/rest/README.md @@ -7,7 +7,7 @@ It uses the following libraries: It requires some simple security configuration to enable you to authenticate to the platform. ## Requirements -- Python 3.9+ +- Python 3.10+ - A client application on Morgan Stanley Azure AD tenant. Please talk to your contact at Morgan Stanley to set this up. - A self-signed public/private key pair. Please see the client setup guide for instructions to generate this. - The thumbprint (also known as fingerprint) for your certificate. @@ -15,18 +15,25 @@ It requires some simple security configuration to enable you to authenticate to ## Configuration Create a file, config.json, with the following properties: - - `client_id`: Your Client Id for the Morgan Stanley API Platform. This is a GUID. - - `scopes`: A list of scopes to request a token against, corresponding to the API you are calling. For help with finding the correct scope, please talk to your Morgan Stanley contact. - - `thumbprint`: The thumbprint (also known as fingerprint) of your certificate, without colon separators. For example `AB48C0D31F95EBF8425AECF3E7E6FA92B34C8D47` - - `private_key_file`: The path to your private key. This can be either an absolute or relative path. For example: `certs/private_key.pem` - - `tenant`: The tenant you are requesting an access token against. - - UAT: `api-uat.morganstanley.com` - - PROD : `api.morganstanley.com` - - `proxy_host`: If you are running this app inside a restricted network environment, you should specify the hostname of the proxy you are using. e.g. `internal-proxy.company.com` - - `proxy_port`: Set this to the port number for your proxy, if applicable. Should be an integer. - - `url`: The URL to call. **NOTE** in a production app you probably won't source the URL like this, but this is just an example. - - `requests_ca_bundle`: The file to use as the CA bundle when verifying HTTPS certificates. If omitted, use the default bundle shipped with the `requests` library. Please see the [SSL validation section](#ssl-validation-issues-and-the-requests-ca-bundle) for more details. - - `disable_ssl_verification`: Explicitly disable SSL verification. **Not recommended for security reasons.** +| Key | Description | +|----------------------|-----------------------------------------------------------------------------------------| +| `client_id` | The client id that will be sent to you from your Morgan Stanley contact | +| `scopes` | The scope/s that will be sent to you from your Morgan Stanley contact | +| `thumbprint` | The certificate thumbprint | +| `private_key_file` | The path to the private_key.pem that has been created | +| `tenant` | The tenant that will be sent to you from your Morgan Stanley contact | +| `url` | The URL of the Morgan Stanley API endpoint you are connecting to | + +### Optional `config.json` Properties + +| Key | Description | Example Value | +|-------------------------|------------------------------------------------------------------------------------------------------|--------------------------------------| +| `proxy_host` | Hostname of your proxy server (if inside a restricted network) | `"internal-proxy.company.com"` | +| `proxy_port` | Port number for your proxy (integer) | `8080` | +| `requests_ca_bundle` | Path to CA bundle file for HTTPS certificate verification. If omitted, uses default from `requests`. | `"/path/to/ca-bundle.pem"` | +| `disable_ssl_verification` | Set to `true` to disable SSL verification (**not recommended for security reasons**). | `false` or `true` | + +See [SSL validation section](#ssl-validation-issues-and-the-requests-ca-bundle) for more details. You can use `config-example.json` as a starting point. @@ -52,7 +59,7 @@ python -m pip install --upgrade pip pip install -r requirements.txt pip install -r dev-requirements.txt -python client-application.py +python client_application.py ``` Mac/Linux: @@ -64,7 +71,7 @@ pip install --upgrade pip pip install -r requirements.txt pip install -r dev-requirements.txt -python client-application.py +python client_application.py ``` The application will launch and connect to the Morgan Stanley API offering and output the result. diff --git a/client-samples/python/rest/config-example.json b/client-samples/python/rest/config-example.json index 8fe763a..44f6d8b 100755 --- a/client-samples/python/rest/config-example.json +++ b/client-samples/python/rest/config-example.json @@ -5,8 +5,8 @@ ], "thumbprint": "CERT THUMBPRINT", "private_key_file": "PATH TO PRIVATE KEY IN PEM FORMAT", - "tenant": "api.morganstanley.com", + "tenant": "TENANT", "proxy_host": "PROXY HOST HERE", "proxy_port": "PROXY PORT", - "url": "https://api.morganstanley.com/rest-example" + "url": "https://example-api.com" } \ No newline at end of file diff --git a/client-samples/python/websockets/README.md b/client-samples/python/websockets/README.md index eb44387..deb97a4 100644 --- a/client-samples/python/websockets/README.md +++ b/client-samples/python/websockets/README.md @@ -7,7 +7,7 @@ It uses the following libraries: It requires some simple security configuration to enable you to authenticate to the platform. ## Requirements -- Python 3.9+ +- Python 3.10+ - A client application on Morgan Stanley Azure AD tenant. Please talk to your contact at Morgan Stanley to set this up. - A self-signed public/private key pair. Please see the Morgan Stanley API Onboarding instructions for help generating these. - The thumbprint (also known as fingerprint) for your certificate. @@ -15,19 +15,24 @@ It requires some simple security configuration to enable you to authenticate to ## Configuration Create a file, config.json, with the following properties: - - `client_id`: Your Client Id for the Morgan Stanley API Platform. This is a GUID. - - `scopes`: A list of scopes to request a token against, corresponding to the API you are calling. For help with finding the correct scope, please talk to your Morgan Stanley contact. - - `thumbprint`: The thumbprint (also known as fingerprint) of your certificate, without colon separators. For example `AB48C0D31F95EBF8425AECF3E7E6FA92B34C8D47` - - `private_key_file`: The path to your private key. This can be either an absolute or relative path. For example: `websockets/private_key.pem` - - `tenant`: The tenant you are requesting an access token against. - - UAT: `api-uat.morganstanley.com` - - PROD : `api.morganstanley.com` - - `proxy_host`: If you are running this app inside a restricted network environment, you should specify the hostname of the proxy you are using. e.g. `internal-proxy.company.com` - - `proxy_port`: Set this to the port number for your proxy, if applicable. Should be an integer. - - `url`: The WebSocket URL to call. **NOTE** in a production app you probably won't source the URL like this, but this is just an example. - - `requests_ca_bundle`: The file to use as the CA bundle when verifying HTTPS certificates. If omitted, use the default bundle shipped with the `requests` library. Please see the [SSL validation section](#ssl-validation-issues-and-the-requests-ca-bundle) for more details. - - `disable_ssl_verification`: Explicitly disable SSL verification. **Not recommended for security reasons.** - - `retry_bad_handshake_status`: Whether or not to retry if the downstream API returns a handshake status other than 101 Switching Protocols. This may indicate an outage on the API and you may not always want to retry in this situation. Default value is `true`. +| Key | Description | +|----------------------|-----------------------------------------------------------------------------------------| +| `client_id` | The client id that will be sent to you from your Morgan Stanley contact | +| `scopes` | The scope/s that will be sent to you from your Morgan Stanley contact | +| `thumbprint` | The certificate thumbprint | +| `private_key_file` | The path to the private_key.pem that has been created | +| `tenant` | The tenant that will be sent to you from your Morgan Stanley contact | +| `url` | The URL of the Morgan Stanley API endpoint you are connecting to | + +### Optional `config.json` Properties + +| Key | Description | Example Value | +|-------------------------|------------------------------------------------------------------------------------------------------|--------------------------------------| +| `proxy_host` | Hostname of your proxy server (if inside a restricted network) | `"internal-proxy.company.com"` | +| `proxy_port` | Port number for your proxy (integer) | `8080` | +| `requests_ca_bundle` | Path to CA bundle file for HTTPS certificate verification. If omitted, uses default from `requests`. | `"/path/to/ca-bundle.pem"` | +| `disable_ssl_verification` | Set to `true` to disable SSL verification (**not recommended for security reasons**). | `false` or `true` | +| `retry_bad_handshake_status` | Whether or not to retry if the downstream API returns a handshake status other than 101 Switching Protocols. This may indicate an outage on the API and you may not always want to retry in this situation. Default value is `true`. | `true` or `false` | You may use [`config-example.json`](./config-example.json) as a starting point. diff --git a/client-samples/python/websockets/config-example.json b/client-samples/python/websockets/config-example.json index 0516c4b..48fb8cc 100755 --- a/client-samples/python/websockets/config-example.json +++ b/client-samples/python/websockets/config-example.json @@ -5,9 +5,9 @@ ], "thumbprint": "CERT THUMBPRINT", "private_key_file": "PATH TO PRIVATE KEY IN PEM FORMAT", - "tenant": "api.morganstanley.com", + "tenant": "TENANT", "proxy_host": "PROXY HOST HERE", "proxy_port": "PROXY PORT", - "url": "wss://ws.api.morganstanley.com/websocket-example", + "url": "wss://example-api.com/websocket-example", "retry_bad_handshake_status": true } \ No newline at end of file