Initial commit

This commit is contained in:
2019-02-05 23:04:30 +01:00
commit 7654ac9585
7 changed files with 324 additions and 0 deletions

107
.gitignore vendored Normal file
View File

@@ -0,0 +1,107 @@
# Created by .ignore support plugin (hsz.mobi)
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.idea/

7
Crx/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
from .connection import Connection
from .simplenode import SimpleNode
from .util import get_simple_con
__version__ = (1, 0, 0, 0)
__all__ = ["Connection", "SimpleNode", "get_simple_con"]

77
Crx/connection.py Normal file
View File

@@ -0,0 +1,77 @@
from typing import Union
import requests
from urllib.parse import urljoin
from .simplenode import SimpleNode
# TODO validation
"""
http://localhost:4502/crx/de/init.jsp?_dc=1549392939742
http://localhost:4502/crx/de/nodetypes.jsp?_dc=1549392939958
http://localhost:4502/crx/server/crx.default/jcr%3aroot/libs.1.json?_dc=1549392123434&node=xnode-265
http://localhost:4502/crx/de/query.jsp?_dc=1549392245191&_charset_=utf-8&type=xpath&stmt=%2Fjcr%3Aroot%2Fbin%2F%2F*%5Bjcr%3Acontains(.%2C%20%27asdf%27)%5D%20order%20by%20%40jcr%3Ascore&showResults=true
"""
CRX_SERVER_ROOT = '/crx/server/crx.default/jcr:root/'
CRX_QUERY = '/crx/de/query.jsp'
JSON_DATA_EXTENSION = '.1.json'
class CrxException(ValueError):
pass
class Connection:
def __init__(self,
host: str = 'localhost',
port: int = 4502,
protocol: str = 'http',
root: str = CRX_SERVER_ROOT,
query: str = CRX_QUERY):
self.host = f'{protocol}://{host}:{port}'
self.data_root = self.host + root
self.query_path = self.host + query
self.session = requests.session()
def login_basic(self, username: str, password: str):
self.session.auth = (username, password)
def get_node_raw(self, path: str):
url = urljoin(self.data_root, '.' + path + JSON_DATA_EXTENSION)
try:
response = self.session.get(url)
except requests.exceptions.RequestException as exception:
raise CrxException() # todo more specific exceptions
try:
data = response.json()
except ValueError:
raise # todo
return data
def get_simple_node(self, path: str) -> SimpleNode:
return SimpleNode(path, self.get_node_raw(path), self)
def rename_node(self, old_path: str, new_path: str):
diff = f'>{old_path} : {new_path}'
resp = self.session.post(self.data_root, data={':diff': diff})
resp.raise_for_status()
def apply_diff(self, diff: Union[str, bytes]):
files = {
':diff': (
None,
diff,
'text/plain; charset=utf-8'
)
}
# todo check for exception
resp = self.session.post(self.data_root, files=files)
resp.raise_for_status()

95
Crx/node.py Normal file
View File

@@ -0,0 +1,95 @@
from copy import copy, deepcopy
import json
from datetime import datetime
def parse_iso_date(date: str) -> datetime:
return datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.000%z')
def fmt_iso_date(date: datetime) -> str:
return date.strftime('%Y-%m-%dT%H:%M:%S.000%z')
PROPERTY_DEFAULT = {
int: 'LONG',
str: 'STRING',
float: 'DOUBLE',
}
"""
type: [external, simple, complex]
content-type[complex]:
"""
PROPERTY_TYPES = {
'BINARY': {
'type': 'complex',
'content-type': 'jcr-value/binary'
},
'BOOLEAN': {
'type': 'simple',
'serialize': json.dumps
},
'DATE': {
'type': 'complex',
'content-type': 'jcr-value/date',
'serialize': fmt_iso_date,
'deserialize': parse_iso_date
},
'DECIMAL': {
'type': 'complex',
'content-type': 'jcr-value/decimal',
'serialize': str,
},
'DOUBLE': {
'type': 'simple',
'serialize': str,
},
'LONG': {
'type': 'simple',
'serialize': str,
},
'NAME': {},
'PATH': {},
'REFERENCE': {},
'STRING': {
'type': 'simple',
'serialize': json.dumps,
'deserialize': json.loads
},
'UNDEFINED': {},
'URI': {},
'WEAKREFERENCE': {},
}
class Property:
pass
class Node:
def __init__(self, path: str, primary_type: str, is_new: bool = True, data: dict = None):
# TODO validate path and type
self.path = path
self.data = data or {}
self.data['jcr:primaryType'] = primary_type
self.original_path = copy(path)
self.original_data = deepcopy(data)
self.is_new = is_new
def _build_diff(self) -> str:
if self.is_new:
return self._build_diff_new()
else:
return self._build_diff_change()
def _build_diff_new(self) -> str:
data = []
ptype_dict = {'jcr:primaryType': self.data['jcr:primaryType']}
data.append(f'+{self.path} : {json.dumps(ptype_dict)}')
for key, value in self.data:
if key == 'jcr:primaryType':
continue # primaryType is handled differently
data.append(f'^{self.path}/{key} : {json.dumps(value)}')
return '\n'.join(data) + '\n'

30
Crx/simplenode.py Normal file
View File

@@ -0,0 +1,30 @@
if False:
from .connection import Connection
class SimpleNode:
def __init__(self, path: str, data: dict, connection: 'Connection'):
self.path = path
self._data = data
self._connection = connection
def __getitem__(self, item):
return getattr(self, item)
def __getattr__(self, item: str):
try:
return super(SimpleNode, self).__getattr__(item)
except AttributeError:
pass
try:
value = self._data.get(item)
except KeyError:
raise AttributeError()
if isinstance(value, dict):
return self._connection.get_simple_node(self.path + '/' + item)
return value
def __dir__(self):
return super(SimpleNode, self).__dir__() + list(self._data.keys())

7
Crx/util.py Normal file
View File

@@ -0,0 +1,7 @@
from .connection import Connection
def get_simple_con():
con = Connection()
con.login_basic('admin', 'admin')
return con

1
requirements.txt Normal file
View File

@@ -0,0 +1 @@
requests >= 2.21.0