Skip to content

Commit

Permalink
Adds Backend Logic to interactions with ZenodoAPI (#50)
Browse files Browse the repository at this point in the history
* added backend features: Sandbox env var, creating deposit, adding title to deposit

* added docker CI logic to tag PR as dev distributions

* added logic for mocking new sandbox variable to tests

* fix

* added asserts for zAPI return statement
  • Loading branch information
mrzengel committed Aug 12, 2024
1 parent 1063eae commit 4e781ef
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 105 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,21 @@ jobs:
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Set image tags
id: set-tags
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "tags=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev" >> $GITHUB_ENV
else
echo "tags=${{ steps.meta.outputs.tags }}" >> $GITHUB_ENV
fi
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v6.0.0
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
tags: ${{ env.tags }}
labels: ${{ steps.meta.outputs.labels }}

# This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see "[AUTOTITLE](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)."
Expand Down
43 changes: 31 additions & 12 deletions src/API/API_functions.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { requestAPI } from './handler';
import { UploadPayload } from '../components/type';

export async function getEnvVariable(varName: string) {
try {
Expand All @@ -17,24 +18,38 @@ export async function setEnvVariable(key: string, value: string) {
method: 'POST',
body: JSON.stringify({ key, value })
});
//console.log(data);
} catch (error) {
console.error(`Error setting ${key}:`, error);
}
}

export async function testZenodoConnection() {
try {
const data = await requestAPI('zenodo-jupyterlab/test-connection', {
/* const data = await requestAPI('zenodo-jupyterlab/test-connection', {
method: 'GET'
});
//console.log(data);
}); */
const data = await requestAPI('zenodo-jupyterlab/zenodo-api', {
method: 'POST',
body: JSON.stringify({action: 'check-connection'}),
})
return data;
} catch (error) {
console.error(`Error testing connection:`, error);
}
}

export async function depositUpload(payload: UploadPayload) {
try {
const data = await requestAPI('zenodo-jupyterlab/zenodo-api', {
method: 'POST',
body: JSON.stringify(payload),
});
return data;
} catch (error) {
console.error('Error uploading info:', error);
}
}

export async function searchRecords(search_field: string, page: number, kwargs: Record<string, any> = {}) {
try {
let url = `zenodo-jupyterlab/search-records?search_field=${encodeURIComponent(search_field)}&page=${encodeURIComponent(page)}`;
Expand Down Expand Up @@ -84,14 +99,18 @@ export async function getServerRootDir() {
}
}

/* export async function runPythonCode(code: string) {
export async function fetchSandboxStatus() {
try {
const data = await requestAPI('zenodo-jupyterlab/code', {
method: 'POST',
body: JSON.stringify({ code: code })
});
console.log(data);
let response = await fetch('zenodo-jupyterlab/env?env_var=ZENODO_SANDBOX');
if (response.ok) {
let data = await response.json();
return data.ZENODO_SANDBOX;
} else {
console.error('Failed to fetch sandbox status');
return null;
}
} catch (error) {
console.error('Error running code:', error);
console.error('Error fetching sandbox status:', error);
return null;
}
} */
}
2 changes: 0 additions & 2 deletions src/components/FileBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,7 @@ const FileBrowser: React.FC<FileBrowserProps> = ({ onSelectFile }) => {
};

const handleBreadcrumbClick = (path: string) => {
console.log('Breadcrumb clicked, path:', path);
if (path !== currentPath) {
//console.log('Updating currentPath from', currentPath, 'to', path);
setCurrentPath(path);
}
};
Expand Down
56 changes: 33 additions & 23 deletions src/components/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,21 @@ const useStyles = createUseStyles({
'&:hover': {
backgroundColor: '#45a049',
},
}
},
checkboxContainer: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: '10px',
flexDirection: 'row',
},
checkboxLabel: {
display: 'flex',
alignItems: 'center',
},
checkboxInput: {
marginRight: '5px',
},
});

const Login: React.FC = () => {
Expand All @@ -50,12 +64,13 @@ const Login: React.FC = () => {
const[outputData, setOutputData] = useState<string | null>(null);
const [connectionStatus, setConnectionStatus] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [isSandbox, setIsSandbox] = useState(false);

const handleLogin = useCallback(async () => {
try {
await setEnvVariable('ZENODO_SANDBOX', String(isSandbox));
if (APIKey != '') {
await setEnvVariable('ZENODO_API_KEY', APIKey);
//console.log(await getEnvVariable('ZENODO_API_KEY'));
setOutputData("Zenodo Token Successfully Stored in Environment.");
testAPIConnection();
} else {
Expand All @@ -70,13 +85,16 @@ const Login: React.FC = () => {
} catch (error) {
console.error(error);
}
}, [APIKey]);
}, [APIKey, isSandbox]);

const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setIsSandbox(event.target.checked);
};

const testAPIConnection = async () => {
setIsLoading(true);
try {
var response = await testZenodoConnection();
//console.log(response['status']);
if (Number(response['status']) == 200) {
setConnectionStatus("API Connection Successful")
} else {
Expand All @@ -89,25 +107,6 @@ const Login: React.FC = () => {
}
}

/* const handleLogin = () => {
try {
var code = `
import os
`;
if (APIKey != '') {
code += `
os.environ['TESTVAR'] = '${APIKey}'
`;
}
code += `
os.environ['TESTVAR']
`;
console.log(code);
} catch (error) {
alert('Invalid Zenodo API Token');
}
} */

return (
<div className={classes.root}>
<div className={classes.loginContainer}>
Expand All @@ -116,6 +115,17 @@ os.environ['TESTVAR']
<input className={classes.input} type="text" id="APIKey" name="APIKey" placeholder="API Key" value={APIKey} onChange={(e) => setAPIKey(e.target.value)} required />
</div>
<div className={classes.formGroup}>
<div className={classes.checkboxContainer}>
<label className={classes.checkboxLabel}>
<input
type="checkbox"
checked={isSandbox}
onChange={handleCheckboxChange}
className={classes.checkboxInput}
/>
Use Sandbox
</label>
</div>
<button className={classes.button} type="submit" onClick={handleLogin}>Login</button>
{outputData ? (
<div>
Expand Down
18 changes: 17 additions & 1 deletion src/components/type.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,20 @@ export interface FileEntry {
size?: number; // Size in bytes
}

export type OnSelectFile = (filePath: string) => void;
export type OnSelectFile = (filePath: string) => void;

export interface Creator {
name: string;
affiliation?: string;
}

export interface UploadPayload {
title: string;
resourceType: string;
creators: Creator[];
doi: string;
description: string;
filePaths: string[];
isSandbox: boolean;
action: string;
}
79 changes: 38 additions & 41 deletions src/components/upload.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { createUseStyles } from 'react-jss';
import FileBrowser from './FileBrowser';
import Confirmation from './confirmation';
import { depositUpload } from '../API/API_functions';
import { UploadPayload } from './type';

const useStyles = createUseStyles({
container: {
Expand Down Expand Up @@ -235,15 +237,19 @@ const Upload: React.FC = () => {
const [description, setDescription] = useState('');
const [expandedFile, setExpandedFile] = useState<string | null>(null);

/* const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const fileList = event.target.files;
if (fileList) {
const newPaths = Array.from(fileList).map(file => file.name);
setSelectedFilePaths(prevPaths => [
...new Set([...prevPaths, ...newPaths]) // Add new paths, avoiding duplicates
]);
useEffect(() => {
async function fetchSandboxStatus() {
try {
const response = await fetch('zenodo-jupyterlab/env?env_var=ZENODO_SANDBOX');
const data = await response.json();
setIsSandbox(data.ZENODO_SANDBOX === 'true');
} catch (error) {
console.error('Error fetching sandbox status:', error);
}
}
}; */

fetchSandboxStatus();
}, []);

const handleFileClick = (filePath: string) => {
setExpandedFile(prev => (prev === filePath ? null : filePath));
Expand All @@ -259,16 +265,10 @@ const Upload: React.FC = () => {
]);
};



const handleTitleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setTitle(event.target.value);
};

const handleSandboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setIsSandbox(event.target.checked);
};

const handleResourceTypeChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
setResourceType(event.target.value);
};
Expand All @@ -295,45 +295,42 @@ const Upload: React.FC = () => {
return;
}
setIsConfirmationVisible(true);
};

const handleEdit = () => {
setIsConfirmationVisible(false);
};

const handleConfirm = async () => {
const formData = new FormData();
selectedFilePaths.forEach(filePath => formData.append('filePaths', filePath));
formData.append('title', title);
formData.append('resourceType', resourceType);
formData.append('creators', JSON.stringify(creators));
formData.append('doi', doi);
formData.append('description', description);
formData.append('action', 'upload');

const payload: UploadPayload = {
title,
resourceType,
creators,
doi,
description,
filePaths: selectedFilePaths,
isSandbox,
action: 'upload'
};

// Example: logging form data
for (let [key, value] of formData.entries()) {
console.log(`${key}: ${value}`);
}

/* // Make an API call here, e.g., using fetch
fetch('your-api-endpoint', {
method: 'POST',
body: formData,
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
}); */
};

const handleEdit = () => {
setIsConfirmationVisible(false);
};

const handleConfirm = () => {
// Make an API call or handle form submission here
console.log('Form submitted with:', { title, resourceType, creators, doi, selectedFilePaths });
// Reset the form or navigate as needed
console.log(JSON.stringify(payload));
const response = await depositUpload(payload);
console.log(response['status']);
};

const fileName = (filePath: string) => {
console.log(filePath);
const segments = filePath.split('/');
return segments.pop();
}
Expand Down Expand Up @@ -368,7 +365,7 @@ const Upload: React.FC = () => {
//onChange={() => handleCheckboxChange('communities')}
className={classes.checkboxInput}
checked={isSandbox}
onChange={handleSandboxChange}
readOnly
/>
Sandbox
</label>
Expand Down
4 changes: 2 additions & 2 deletions zenodo_jupyterlab/server/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ def _load_jupyter_server_extension(server_app):
try:
setup_handlers(web_app)
server_app.log.info("Registered zenodo_jupyterlab server extension")
except Exception as e:
server_app.log.error(f"Failed to register zenodo_jupyterlab server extension: {e}")
except:
server_app.log.error(f"Failed to register zenodo_jupyterlab server extension")
raise
Loading

0 comments on commit 4e781ef

Please sign in to comment.