Skip to content
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

Release 1.2.0 #6

Merged
merged 7 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
125 changes: 62 additions & 63 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,77 +10,76 @@ jobs:
runs-on: ubuntu-latest

env:
PUBLIC_IMAGE: datastewardshipwizard/nanopub-submission-service
PRIVATE_IMAGE: ${{ secrets.PRIVATE_REGISTRY_URL }}/nanopub-submission-service
TAG_DEVELOP: develop
TAG_LATEST: latest
PUBLIC_IMAGE_PREFIX: 'datastewardshipwizard'
DOCKER_IMAGE_NAME: 'nanopub-submission-service'
DOCKER_META_CONTEXT: '.'
DOCKER_META_FILE: 'Dockerfile'
DOCKER_META_PLATFORMS: 'linux/amd64,linux/arm64'

steps:
- uses: actions/checkout@v2
- name: '[setup] Check out repository'
uses: actions/checkout@v4
with:
fetch-depth: 0

# (1) -> Build Docker image
- name: Update build info
run: |
./scripts/build-info.sh
- name: '[setup] Set up QEMU'
uses: docker/setup-qemu-action@v3

- name: Docker build
run: |
docker build -t $PRIVATE_IMAGE:$GITHUB_SHA .
- name: '[setup] Set up Docker Buildx'
id: buildx
uses: docker/setup-buildx-action@v3

# (2) -> Docker image tagging
- name: Docker login
if: github.event_name == 'push'
- name: '[setup ]Update build info'
run: |
docker login -u "$DOCKER_HUB_USERNAME" -p "$DOCKER_HUB_PASSWORD"
docker login -u "$PRIVATE_REGISTRY_USERNAME" -p "$PRIVATE_REGISTRY_PASSWORD" "$PRIVATE_REGISTRY_URL"
env:
DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }}
DOCKER_HUB_PASSWORD: ${{ secrets.DOCKER_HUB_PASSWORD }}
PRIVATE_REGISTRY_URL: ${{ secrets.PRIVATE_REGISTRY_URL }}
PRIVATE_REGISTRY_USERNAME: ${{ secrets.PRIVATE_REGISTRY_USERNAME }}
PRIVATE_REGISTRY_PASSWORD: ${{ secrets.PRIVATE_REGISTRY_PASSWORD }}
./scripts/build-info.sh

- name: Docker push - commit SHA (private)
if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')
run: |
docker push $PRIVATE_IMAGE:$GITHUB_SHA
- name: '[docker] Docker meta'
id: meta-test
uses: docker/metadata-action@v5
with:
images: |
${{ env.PUBLIC_IMAGE_PREFIX }}/${{ env.DOCKER_IMAGE_NAME }}
tags: |
type=sha

- name: Docker tag and push - branch (private)
if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/') && !contains(github.ref, 'release')
run: |
GITHUB_BRANCH=`echo $GITHUB_REF | cut -d/ -f3- | sed 's#/#-#g'`
docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $PRIVATE_IMAGE:$GITHUB_BRANCH
docker push $PRIVATE_IMAGE:$GITHUB_BRANCH
- name: '[docker] Docker build'
uses: docker/build-push-action@v4
with:
context: ${{ env.DOCKER_META_CONTEXT }}
file: ${{ env.DOCKER_META_FILE }}
platforms: ${{ env.DOCKER_META_PLATFORMS }}
push: false
tags: ${{ steps.meta-test.outputs.tags }}
labels: ${{ steps.meta-test.outputs.labels }}

- name: Docker tag and push - develop (public)
if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
run: |
docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $PUBLIC_IMAGE:$TAG_DEVELOP
docker push $PUBLIC_IMAGE:$TAG_DEVELOP
- name: '[docker-hub] Docker login'
if: github.event_name != 'pull_request' && github.actor != 'dependabot[bot]'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}

- name: Docker tag and push - latest (public)
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
run: |
docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $PUBLIC_IMAGE:$TAG_LATEST
docker push $PUBLIC_IMAGE:$TAG_LATEST
- name: '[docker-hub] Docker meta'
id: meta-public
if: github.event_name != 'pull_request' && github.actor != 'dependabot[bot]'
uses: docker/metadata-action@v5
with:
images: |
${{ env.PUBLIC_IMAGE_PREFIX }}/${{ env.DOCKER_IMAGE_NAME }}
tags: |
type=ref,event=branch
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }}

- name: Docker tag and push - version tag (public)
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
run: |
GITHUB_TAG=`echo $GITHUB_REF | cut -d/ -f3`
# Release vX.Y.Z
if [[ $GITHUB_TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
IMAGE_TAG_MAJOR="$PUBLIC_IMAGE:"`echo $GITHUB_TAG | sed -E "s/v(.*)\..*\..*/\1/g"`
IMAGE_TAG_MINOR="$PUBLIC_IMAGE:"`echo $GITHUB_TAG | sed -E "s/v(.*)\..*/\1/g"`
IMAGE_TAG_PATCH="$PUBLIC_IMAGE:"`echo $GITHUB_TAG | sed -E "s/v//g"`
echo "Publishing release: $IMAGE_TAG_PATCH";
docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $IMAGE_TAG_MAJOR && docker push $IMAGE_TAG_MAJOR;
docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $IMAGE_TAG_MINOR && docker push $IMAGE_TAG_MINOR;
docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $IMAGE_TAG_PATCH && docker push $IMAGE_TAG_PATCH;
fi
# Release candidate vX.Y.Z-rc.R
if [[ $GITHUB_TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then
IMAGE_TAG_RC="$PUBLIC_IMAGE:"`echo $GITHUB_TAG | sed -E "s/v//g"`
echo "Publishing release candidate: $IMAGE_TAG_RC";
docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $IMAGE_TAG_RC && docker push $IMAGE_TAG_RC;
fi
- name: '[docker-hub] Docker build+push'
uses: docker/build-push-action@v4
if: github.event_name != 'pull_request' && steps.meta-public.outputs.tags != ''
with:
context: ${{ env.DOCKER_META_CONTEXT }}
file: ${{ env.DOCKER_META_FILE }}
platforms: ${{ env.DOCKER_META_PLATFORMS }}
push: true
tags: ${{ steps.meta-public.outputs.tags }}
labels: ${{ steps.meta-public.outputs.labels }}
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM python:3.9-slim-buster
FROM python:3.11-slim-bookworm

RUN apt-get update && \
apt install -y --no-install-recommends bash default-jre && \
apt install -y --no-install-recommends bash default-jre build-essential gcc && \
rm -rf /var/lib/apt/lists/*

WORKDIR /app
Expand Down
Binary file removed bin/nanopub-1.36-jar-with-dependencies.jar
Binary file not shown.
Binary file added bin/nanopub-1.55-jar-with-dependencies.jar
Binary file not shown.
1 change: 0 additions & 1 deletion bin/np
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,3 @@ fi

>&2 echo "ERROR: Failed to find or download nanopub jar file."
exit 1

26 changes: 19 additions & 7 deletions nanopub_submitter/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

from typing import Tuple

from nanopub_submitter.config import cfg_parser
from nanopub_submitter.consts import NICE_NAME, VERSION, BUILD_INFO,\
from nanopub_submitter.config import cfg_parser, RequestConfig
from nanopub_submitter.consts import NICE_NAME, VERSION, BUILD_INFO, \
ENV_CONFIG, DEFAULT_CONFIG, DEFAULT_ENCODING
from nanopub_submitter.logger import LOG, init_default_logging, init_config_logging
from nanopub_submitter.mailer import Mailer
Expand Down Expand Up @@ -43,6 +43,12 @@ def _extract_content_type(header: str) -> Tuple[str, str]:
return input_format, DEFAULT_ENCODING


def _extract_servers(header: str) -> list[str]:
if header == '':
return []
return list(map(lambda x: x.strip(), header.split(',')))


@app.get(path='/')
async def get_info():
return fastapi.responses.JSONResponse(
Expand All @@ -62,7 +68,11 @@ async def submit_nanopub(request: fastapi.Request):
# (2) Extract data
submission_id = str(uuid.uuid4())
data = await request.body()
input_format, encoding = _extract_content_type(request.headers.get('Content-Type'))
input_format, encoding = _extract_content_type(request.headers.get('Content-Type', ''))
req_cfg = RequestConfig(
servers=_extract_servers(request.headers.get('X-NP-Servers', '')),
uri_replace=request.headers.get('X-URI-Replace', None)
)
if input_format != 'application/trig':
return fastapi.responses.Response(
status_code=fastapi.status.HTTP_400_BAD_REQUEST,
Expand All @@ -73,6 +83,7 @@ async def submit_nanopub(request: fastapi.Request):
try:
result = process(
cfg=cfg,
req_cfg=req_cfg,
submission_id=submission_id,
data=data.decode(encoding),
)
Expand All @@ -85,16 +96,17 @@ async def submit_nanopub(request: fastapi.Request):
LOG.error(f'[{submission_id}] Unexpected processing error: {str(e)}')
return fastapi.responses.PlainTextResponse(
status_code=fastapi.status.HTTP_500_INTERNAL_SERVER_ERROR,
content='Failed to process the nanopublication',
content=f'Failed to process the nanopublication: {str(e)}',
)
# (4) Mail
Mailer.get().notice(nanopub_uri=result.location)
# (5) Return
headers = dict()
if result.location is not None:
headers['Location'] = result.location
return fastapi.responses.Response(
status_code=fastapi.status.HTTP_201_CREATED,
headers={
'Location': result.location,
},
headers=headers,
content=str(result),
)

Expand Down
7 changes: 7 additions & 0 deletions nanopub_submitter/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,10 @@ def config(self) -> SubmitterConfig:


cfg_parser = SubmitterConfigParser()


class RequestConfig:

def __init__(self, servers: list[str], uri_replace: Optional[str]):
self.servers = servers
self.uri_replace = uri_replace
2 changes: 1 addition & 1 deletion nanopub_submitter/consts.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
PACKAGE_NAME = 'nanopub_submitter'
NICE_NAME = 'DSW Nanopublication Submission Service'
PACKAGE_VERSION = '1.1.0'
PACKAGE_VERSION = '1.2.0'
ENV_CONFIG = 'SUBMISSION_CONFIG'
LOGGER_NAME = 'DSW_SUBMITTER'

Expand Down
69 changes: 48 additions & 21 deletions nanopub_submitter/nanopub.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from typing import Optional, Tuple

from nanopub_submitter.config import SubmitterConfig
from nanopub_submitter.config import SubmitterConfig, RequestConfig
from nanopub_submitter.consts import DEFAULT_ENCODING, PACKAGE_NAME, PACKAGE_VERSION
from nanopub_submitter.logger import LOG
from nanopub_submitter.triple_store import store_to_triple_store
Expand Down Expand Up @@ -40,9 +40,11 @@ def __str__(self):

class NanopubProcessingContext:

def __init__(self, submission_id: str, cfg: SubmitterConfig):
def __init__(self, submission_id: str, cfg: SubmitterConfig,
req_cfg: RequestConfig):
self.id = submission_id
self.cfg = cfg
self.req_cfg = req_cfg
self.uri = None

def cleanup(self):
Expand All @@ -54,6 +56,20 @@ def cleanup(self):
except Exception:
pass

@property
def target_servers(self) -> list[str]:
if len(self.req_cfg.servers) > 0:
return self.req_cfg.servers
return self.cfg.nanopub.target_servers

@property
def uri_replace(self) -> Optional[str]:
if self.req_cfg.uri_replace is None:
return self.cfg.nanopub.uri_replace
if '|' in self.req_cfg.uri_replace:
return self.req_cfg.uri_replace
return None

@property
def input_file(self) -> str:
return f'{self.id}.trig'
Expand Down Expand Up @@ -100,23 +116,29 @@ def _split_nanopubs(nanopub_bundle: str) -> list[str]:
def _publish_nanopub(nanopub_bundle: str, ctx: NanopubProcessingContext) -> list[str]:
success = []
nanopubs = _split_nanopubs(nanopub_bundle)
for server in ctx.cfg.nanopub.target_servers:
for server in ctx.target_servers:
ctx.debug(f'Submitting to: {server}')
ok = True
for nanopub in nanopubs:
r = requests.post(
url=server,
data=nanopub,
headers={
'Content-Type': f'application/trig; charset={DEFAULT_ENCODING}',
'User-Agent': f'{PACKAGE_NAME}/{PACKAGE_VERSION}',
}
)
if not r.ok:
try:
r = requests.post(
url=server,
data=nanopub.encode(encoding=DEFAULT_ENCODING),
headers={
'Content-Type': f'application/trig; charset={DEFAULT_ENCODING}',
'User-Agent': f'{PACKAGE_NAME}/{PACKAGE_VERSION}',
},
timeout=10,
)
if not r.ok:
ok = False
ctx.warn(f'Failed to publish nanopub via {server}')
ctx.debug(f'status={r.status_code}')
ctx.debug(r.text)
break
except Exception as e:
ok = False
ctx.warn(f'Failed to publish nanopub via {server}')
ctx.debug(f'status={r.status_code}')
ctx.debug(r.text)
ctx.warn(f'Failed to publish nanopub via {server}: {str(e)}')
break
if ok:
ctx.info(f'Nanopub published via {server}')
Expand Down Expand Up @@ -173,10 +195,10 @@ def _run_np_sign(ctx: NanopubProcessingContext) -> str:
ctx=ctx,
)
if exit_code != EXIT_SUCCESS:
LOG.warn(f'Failed to make TrustyURI ({exit_code}):\n{stdout}\n\n{stderr}')
LOG.warn(f'Failed to sign the nanopub ({exit_code}):\n{stdout}\n\n{stderr}')
raise NanopubProcessingError(
status_code=500,
message='Failed to make TrustyURI for nanopub.'
message='Failed to sign the nanopub.'
)
return ctx.signed_file

Expand All @@ -192,8 +214,13 @@ def _extract_np_uri(nanopub: str) -> Optional[str]:
return last_this_prefix


def process(cfg: SubmitterConfig, submission_id: str, data: str) -> NanopubSubmissionResult:
ctx = NanopubProcessingContext(submission_id=submission_id, cfg=cfg)
def process(cfg: SubmitterConfig, req_cfg: RequestConfig,
submission_id: str, data: str) -> NanopubSubmissionResult:
ctx = NanopubProcessingContext(
submission_id=submission_id,
cfg=cfg,
req_cfg=req_cfg,
)
ctx.debug('Preprocessing nanopublication as RDF')
try:
graph = rdflib.ConjunctiveGraph()
Expand Down Expand Up @@ -234,8 +261,8 @@ def process(cfg: SubmitterConfig, submission_id: str, data: str) -> NanopubSubmi
ctx.cleanup()
raise NanopubProcessingError(400, 'Failed to get nanopub URI')

if cfg.nanopub.uri_replace is not None:
old, new = cfg.nanopub.uri_replace.split('|', maxsplit=1)
if ctx.uri_replace is not None:
old, new = ctx.uri_replace.split('|', maxsplit=1)
new_uri = nanopub_uri.replace(old, new)
LOG.debug(f'Replacing {nanopub_uri} with {new_uri}')
nanopub_uri = new_uri
Expand Down
Loading
Loading