Skip to content

Release 1.2.0 #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
208ebae
Added TOC and info on kwargs for eval/rows
rjrudin Nov 7, 2023
b055cc5
Merge pull request #54 from marklogic/feature/more-docs
rjrudin Nov 7, 2023
fada978
DEVEXP-639: Submit Optic Update DSL plan via Python Client
BillFarber Dec 14, 2023
d1b3480
Merge pull request #57 from BillFarber/feature/rowsUpdate
BillFarber Dec 14, 2023
d67c98f
Add a new Optic DSL Update test to demonstrate including permissions.
BillFarber Dec 14, 2023
3ddeb8a
Merge pull request #58 from BillFarber/task/updateOpticDslTest
BillFarber Dec 14, 2023
ff6e3b7
Update the linting and formatting settings.
BillFarber Dec 14, 2023
4697b9f
Merge pull request #59 from BillFarber/task/updateLintingAndFormatting
rjrudin Dec 14, 2023
6767475
Fixing Optic update test
rjrudin Jan 4, 2024
6482dc3
Can now perform update with serialized plan
rjrudin Jan 4, 2024
35aca13
Merge pull request #60 from marklogic/feature/test-fix
rjrudin Jan 5, 2024
1f8052b
Merge pull request #61 from marklogic/feature/update-via-plan
rjrudin Jan 5, 2024
1f8b6e8
MLE-4001: Adding pipeline for python client
SameeraPriyathamTadikonda Mar 18, 2024
b020637
Merge pull request #64 from SameeraPriyathamTadikonda/develop
rjrudin Mar 19, 2024
78f2c8c
Merge branch 'master' into develop
rjrudin Mar 20, 2024
6461e72
Test fixes
rjrudin Mar 20, 2024
f475498
Fixes the bug identified in https://progresssoftware.atlassian.net/br…
BillFarber Jun 10, 2024
3969dca
Merge pull request #65 from BillFarber/develop
BillFarber Jun 10, 2024
e15134f
Prep for 1.2.0 release
BillFarber Jun 10, 2024
4185178
Merge pull request #67 from BillFarber/develop
BillFarber Jun 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"python.formatting.provider": "black",
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.linting.flake8Args": [
"--max-line-length=88"
],
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true
},
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"flake8.args": [
"--max-line-length=120"
]
}
23 changes: 22 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@ To try this out locally:
- `source .venv/bin/activate` to use that virtual environment.
- `poetry install` to install project dependencies.

VSCode is recommended for development. You can try [these instructions](https://www.pythoncheatsheet.org/blog/python-projects-with-poetry-and-vscode-part-1)
VSCode is recommended for development.
- For formatting, the project uses the [Black](https://github.com/psf/black) code formatter, and the
[Black Formatter VSCode extension](https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter)
is recommended.
- For linting, the project uses the [Flake8](https://flake8.pycqa.org/en/latest/) linter and the
[Flake8 extension](https://marketplace.visualstudio.com/items?itemName=ms-python.flake8) is recommended.
These tools are included in the project by pyproject.toml and the settings are in .vscode/settings.json.

You can also get additional information at
[these instructions](https://www.pythoncheatsheet.org/blog/python-projects-with-poetry-and-vscode-part-1)
for getting setup in VSCode with linting and formatting enabled.

## Running the tests
Expand Down Expand Up @@ -67,6 +76,18 @@ project's documentation:

python -i shell/docs.py


## Testing updates in a different local project
If you are using this in another project and making changes for it, use the following command to make the
changes to this local project immediately reflected in a dependent project:
```poetry add <local-path-to-this-project>/marklogic-python-client/```

Using this method will allow you to very easily test changes to this project, in a different local project.

Keep in mind that you probably do not want to check that version of the pyproject.toml file into version
control since it is only useful locally.


## Testing the documentation locally

The docs for this project are stored in the `./docs` directory as a set of Markdown files. These are published via
Expand Down
39 changes: 39 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@Library('shared-libraries') _
pipeline{
agent none;
environment{
JAVA_HOME_DIR="/home/builder/java/jdk-11.0.2"
GRADLE_DIR =".gradle"
}
options {
checkoutToSubdirectory 'marklogic-python-client'
buildDiscarder logRotator(artifactDaysToKeepStr: '7', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '')
}
stages{
stage('tests'){
agent {label 'devExpLinuxPool'}
steps{
script{
copyRPM 'Latest','11'
setUpML '$WORKSPACE/xdmp/src/Mark*.rpm'
sh label:'deploy project', script: '''#!/bin/bash
export JAVA_HOME=$JAVA_HOME_DIR
export GRADLE_USER_HOME=$WORKSPACE/$GRADLE_DIR
export PATH=$GRADLE_USER_HOME:$JAVA_HOME/bin:$PATH
cd marklogic-python-client/test-app
./gradlew -i mlDeploy -PmlPassword=admin
'''
sh label:'Run tests', script: '''#!/bin/bash
cd marklogic-python-client
python -m venv .venv;
source .venv/bin/activate;
pip install poetry;
poetry install;
pytest --junitxml=TestReport.xml || true
'''
junit 'marklogic-python-client/TestReport.xml'
}
}
}
}
}
6 changes: 6 additions & 0 deletions docs/creating-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ The `Client` class is the primary API to interact with in the MarkLogic Python c
found in both the `Session` class and the `requests` API. You can therefore use a `Client` object in the same manner
as you'd use either the `Session` class or the `requests` API.

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

## Creating a client

A `Client` instance can be created either by providing a base URL for all requests along with authentication:
Expand Down
20 changes: 20 additions & 0 deletions docs/eval.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ execution of custom code, whether via an inline script or an existing module in
The MarkLogic Python client supports execution of custom code by simplifying the submission of custom code
and converting the multipart response into more useful Python data types.

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

## Setup

The examples below all depend on the instructions in the [setup guide](example-setup.md) having already been performed.
Expand Down Expand Up @@ -117,3 +123,17 @@ processing of the response or debugging requests.
The `client.eval` and `client.invoke` functions both support referencing a
[REST API transaction](https://docs.marklogic.com/REST/client/transaction-management) via the `tx`
argument. See [the guide on transactions](transactions.md) for further information.

## Providing additional arguments

The `client.eval` and `client.invoke` methods each provide a `**kwargs` argument, so you can pass in any other arguments you would
normally pass to `requests`. For example:

```
client.eval(javascript="fn.currentDateTime()", params={"database": "Documents"})
client.invoke("/sample.sjs", params={"database": "Documents"})
```

Please see [the eval endpoint documentation](https://docs.marklogic.com/REST/POST/v1/eval)
and [the invoke endpoint documentation](https://docs.marklogic.com/REST/POST/v1/invoke) for
information on additional parameters.
6 changes: 6 additions & 0 deletions docs/managing-documents/reading.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ The [GET /v1/documents](https://docs.marklogic.com/REST/GET/v1/documents) endpoi
reading multiple documents with metadata via a multipart/mixed HTTP response. The MarkLogic Python client simplifies
handling the response by converting it into a list of `Document` instances via the `client.documents.read` method.

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

## Setup for examples

The examples below all assume that you have created a new MarkLogic user named "python-user" as described in the
Expand Down
6 changes: 6 additions & 0 deletions docs/managing-documents/searching.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ returning content and metadata for each matching document. Similar to reading mu
HTTP response. The MarkLogic Python client simplifies use of this operation by returning a list of `Document` instances
via the `client.documents.search` method.

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

## Setup for examples

The examples below all assume that you have created a new MarkLogic user named "python-user" as described in the
Expand Down
8 changes: 8 additions & 0 deletions docs/managing-documents/writing.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ writing multiple documents with metadata via a multipart HTTP request. The MarkL
simplifies the use of this endpoint via the `client.documents.write` method and the `Document`
class.

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

## Setup

The examples below all assume that you have created a new MarkLogic user named "python-user" as described in the
[setup guide](../example-setup.md). In addition, each of the examples below requires the following `Client` instance to be created
first:
Expand Down
22 changes: 21 additions & 1 deletion docs/rows.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ The [MarkLogic REST rows service](https://docs.marklogic.com/REST/client/row-man
operations for querying for rows via several query languages. The MarkLogic Python client simplifies submitting queries
for rows and converting responses into useful data structures.

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

## Setup

The examples below require documents to be loaded along with a
Expand Down Expand Up @@ -178,4 +184,18 @@ Printing the `df` object will yield the following:
1 Davis Miles 1926-05-26
2 Armstrong Louis 1901-08-04
3 Coltrane John 1926-09-23
```
```

## Providing additional arguments

The `client.rows.query` method provides a `**kwargs` argument, so you can pass in any other arguments you would
normally pass to `requests`. For example:

```
response = client.rows.query("op.fromView('example', 'musician')", params={"database": "Documents"})
```

Please see [the rows endpoint documentation](https://docs.marklogic.com/REST/POST/v1/rows) for
information on additional parameters. If you are submitting a GraphQL query, then see
[the GraphQL endpoint documentation](https://docs.marklogic.com/REST/POST/v1/rows/graphql) for
information on parameters for that endpoint.
8 changes: 8 additions & 0 deletions docs/transactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ via a `Transaction` class that is also a
thereby allowing it to handle committing or rolling back the transaction without any user
involvement.

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

## Using a transaction

The following example demonstrates writing documents via multiple calls to MarkLogic,
all within the same REST API transaction; the example depends on first following the
instructions in the [setup guide](example-setup.md):
Expand Down
33 changes: 20 additions & 13 deletions marklogic/documents.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
from collections import OrderedDict
from email.message import Message
from typing import Union

from marklogic.transactions import Transaction
Expand Down Expand Up @@ -262,23 +263,29 @@ def _extract_values_from_header(part) -> dict:
Returns a dict containing values about the document content or metadata.
"""
encoding = part.encoding
disposition = part.headers["Content-Disposition".encode(encoding)].decode(encoding)
disposition_values = {}
for item in disposition.split(";"):
tokens = item.split("=")
# The first item will be "attachment" and can be ignored.
if len(tokens) == 2:
disposition_values[tokens[0].strip()] = tokens[1]
disposition = part.headers["Content-Disposition".encode(encoding)].decode(
encoding
)

content_type = None
if part.headers.get("Content-Type".encode(encoding)):
content_type = part.headers["Content-Type".encode(encoding)].decode(encoding)
content_type = part.headers["Content-Type".encode(encoding)].decode(
encoding
)

uri = disposition_values["filename"]
if uri.startswith('"'):
uri = uri[1:]
if uri.endswith('"'):
uri = uri[:-1]
content_disposition_header = part.headers[
"Content-Disposition".encode(encoding)
].decode(encoding)
msg = Message()
msg["content-disposition"] = content_disposition_header
uri = msg.get_filename()

disposition_values = {}
for item in disposition.replace(uri, "").split(";"):
tokens = item.split("=")
key = tokens[0].strip()
if key in ["category", "versionId"]:
disposition_values[key] = tokens[1]

return {
"uri": uri,
Expand Down
57 changes: 57 additions & 0 deletions marklogic/rows.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,63 @@ def query(
not 2xx, then the entire response is always returned.
"""
path = "v1/rows/graphql" if graphql else "v1/rows"
return self.__send_request(
path,
dsl,
plan,
sql,
sparql,
graphql,
format,
tx,
return_response,
**kwargs,
)

def update(
self,
dsl: str = None,
plan: dict = None,
format: str = "json",
tx: Transaction = None,
return_response: bool = False,
**kwargs,
):
"""
Sends an update query to an endpoint at the MarkLogic rows service defined at
https://docs.marklogic.com/REST/client/row-management. One of 'dsl' or
'plan' must be defined. This feature requires the use of MarkLogic version
11.2 or later.

For more information about Optic Update and using the Optic DSL,
see https://docs.marklogic.com/guide/app-dev/OpticAPI.

:param dsl: an Optic DSL query
:param plan: a serialized Optic query
:param tx: optional REST transaction in which to service this request.
:param return_response: boolean specifying if the entire original response
object should be returned (True) or if only the data should be returned (False)
upon a success (2xx) response. Note that if the status code of the response is
not 2xx, then the entire response is always returned.
"""
path = "v1/rows/update"
return self.__send_request(
path, dsl, plan, None, None, None, format, tx, return_response, **kwargs
)

def __send_request(
self,
path: str = None,
dsl: str = None,
plan: dict = None,
sql: str = None,
sparql: str = None,
graphql: str = None,
format: str = "json",
tx: Transaction = None,
return_response: bool = False,
**kwargs,
):
headers = kwargs.pop("headers", {})
data = None
if graphql:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "marklogic-python-client"
version = "1.1.0"
version = "1.2.0"
description = "Python client for MarkLogic, built on the requests library"
authors = ["MarkLogic <[email protected]>"]
readme = "README.md"
Expand Down
1 change: 1 addition & 0 deletions test-app/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.gradle
gradle-local.properties
build
docker
18 changes: 18 additions & 0 deletions test-app/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: '3.8'
name: marklogic_python

services:

marklogic:
image: "marklogicdb/marklogic-db:11.2.0-centos-1.1.2"
platform: linux/amd64
environment:
- INSTALL_CONVERTERS=true
- MARKLOGIC_INIT=true
- MARKLOGIC_ADMIN_USERNAME=admin
- MARKLOGIC_ADMIN_PASSWORD=admin
volumes:
- ./docker/marklogic/logs:/var/opt/MarkLogic/Logs
ports:
- "8000-8002:8000-8002"
- "8030-8031:8030-8031"
1 change: 1 addition & 0 deletions test-app/src/main/ml-data/doc2;copy.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<hello>semicolon</hello>
1 change: 1 addition & 0 deletions test-app/src/main/ml-data/doc2=copy.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<hello>equal</hello>
Loading
Loading