Skip to content

Commit

Permalink
feat(client): add migrations for anonymous users
Browse files Browse the repository at this point in the history
- Allow anonymous users to invoke renku-core APIs (where possible)
- Switch to using git_url + branch for migrations instead of project_id

BREAKING CHANGE: requires SwissDataScienceCenter/renku-python#2541

re #1507
  • Loading branch information
lorenzo-cavazzi committed Jan 7, 2022
1 parent a892d34 commit d5d4931
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 212 deletions.
127 changes: 45 additions & 82 deletions client/src/api-client/migration.js
Original file line number Diff line number Diff line change
@@ -1,106 +1,69 @@
export default function addMigrationMethods(client) {

// TODO: merge this with getProjectIdFromCoreService
client.getProjectIdFromService = async (projectUrl) => {
let headers = client.getBasicHeaders();
headers.append("Content-Type", "application/json");
headers.append("X-Requested-With", "XMLHttpRequest");

// If the project is in the cache, get the id from the cache
let projectsList = await client.clientFetch(`${client.baseUrl}/renku/cache.project_list`, {
method: "GET",
headers: headers
});

if (projectsList.data != null && projectsList.data.error == null) {
const project = projectsList.data.result.projects
.find(project => project.git_url === projectUrl);
if (project != null)
return project.project_id;
}

// Otherwise clone it and get the id
let project = await client.clientFetch(`${client.baseUrl}/renku/cache.project_clone`, {
method: "POST",
headers: headers,
body: JSON.stringify({
depth: 1,
git_url: projectUrl
})
});

if (project.data !== undefined && project.data.error !== undefined)
return project;
return project.data.result.project_id;
};

// TODO: switch to git_url + branch for migrations. Requires SwissDataScienceCenter/renku-python#2541
client.checkMigration = async (git_url, branch) => {
/**
* Check migration status of a project.
*
* @param {string} git_url - Project repository URL.
* @param {string} [branch] - Target branch.
* @returns {object} migration data.
*/
client.checkMigration = async (git_url, branch = null) => {
let headers = client.getBasicHeaders();
headers.append("Content-Type", "application/json");
headers.append("X-Requested-With", "XMLHttpRequest");
const url = `${client.baseUrl}/renku/cache.migrations_check`;
const queryParams = { git_url, branch };
let queryParams = { git_url };
if (branch)
queryParams.branch = branch;

return await client.clientFetch(url, {
method: "GET",
headers,
queryParams
});
}).then(response => response?.data ? response.data : response);
};

client.performMigrationCheck = async (projectId) => {
let headers = client.getBasicHeaders();
headers.append("Content-Type", "application/json");
headers.append("X-Requested-With", "XMLHttpRequest");
const url = `${client.baseUrl}/renku/cache.migrations_check`;
const queryParams = { project_id: projectId };

try {
return await client.clientFetch(url, {
method: "GET",
headers,
queryParams
});
}
catch (error) {
return { data: { error: { reason: error.case } } };
}
};

/**
* Performs project migrations
* Migrate target project.
*
* - force_template_update: set to true to update the template even
* if automated_template_update is not set on the template (probably not a good idea...)
* - skip_template_update: don't try to update the template (superseedes force_template_update)
* - skip_docker_update: don't try to update the Dockerfile.
* - skip_migrations: don't execute migrations.
* @param {string} git_url - Project repository URL.
* @param {string} [branch] - Target branch.
* @param {object} [options] - Migration options.
* @param {boolean} [options.force_template_update] - set to true to update the template even
* if automated_template_update is not set on the template (usually not a good idea)
* @param {boolean} [options.skip_template_update] - do not update the template
* (superseedes force_template_update)
* @param {boolean} [options.skip_docker_update] - do not update the Dockerfile.
* @param {boolean} [options.skip_migrations] - do not execute migrations
* @returns {object} migration data.
*/

client.performMigration = (projectId,
{ force_template_update = false,
skip_template_update = false,
skip_docker_update = false,
skip_migrations = false }
) => {
client.migrateProject = async (git_url, branch = null, options = {
force_template_update: false,
skip_template_update: false,
skip_docker_update: false,
skip_migrations: false
}) => {
let headers = client.getBasicHeaders();
headers.append("Content-Type", "application/json");
headers.append("X-Requested-With", "XMLHttpRequest");
const url = `${client.baseUrl}/renku/cache.migrate`;
let body = {
git_url,
force_template_update: options.force_template_update,
skip_template_update: options.skip_template_update,
skip_docker_update: options.skip_docker_update,
skip_migrations: options.skip_migrations
};
if (branch)
body.branch = branch;

return client.clientFetch(`${client.baseUrl}/renku/cache.migrate`, {
return await client.clientFetch(url, {
method: "POST",
headers: headers,
body: JSON.stringify({
project_id: projectId,
force_template_update,
skip_template_update,
skip_docker_update,
skip_migrations
})
}).catch((error)=>
({ data: { error: { reason: error.case } }
}));
headers,
body: JSON.stringify(body)
})
.then(response => response?.data ? response.data : response)
.catch(error => ({ error: { reason: error.case } }));
};

}
1 change: 1 addition & 0 deletions client/src/dataset/Dataset.present.js
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ export default function DatasetView(props) {
{
props.logged ?
<AddDataset
httpProjectUrl={props.httpProjectUrl}
client={props.client}
dataset={dataset}
formLocation={props.location.pathname + "/add"}
Expand Down
34 changes: 12 additions & 22 deletions client/src/dataset/addtoproject/DatasetAdd.container.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,31 +137,21 @@ function AddDataset(props) {
const selectedProject = projectOptions.find((project)=>
project.value === mappedInputs.project);

props.client.getProjectIdFromService(selectedProject.value)
.then((response) => {
if (response.data && response.data.error !== undefined) {
handlers.setSubmitLoader({ value: false, text: "" });
handlers.setServerErrors(response.data.error.reason);
props.client.checkMigration(props.httpProjectUrl).then((response) => {
if (response && response.error !== undefined) {
handlers.setSubmitLoader({ value: false, text: "" });
handlers.setServerErrors(response.error.reason);
}
else {
if (response.result.migration_required) {
handlers.setServerWarnings(selectedProject.name);
handlers.setSubmitLoader(false);
}
else {
props.client.performMigrationCheck(response)
.then((response) => {
if (response.data && response.data.error !== undefined) {
handlers.setSubmitLoader({ value: false, text: "" });
handlers.setServerErrors(response.data.error.reason);
}
else {
if (response.data.result.migration_required) {
handlers.setServerWarnings(selectedProject.name);
handlers.setSubmitLoader(false);
}
else {
importDataset(selectedProject, handlers);
}
}
});
importDataset(selectedProject, handlers);
}
});
}
});
};

const initializeFunction = (formSchema, formHandlers) => {
Expand Down
41 changes: 21 additions & 20 deletions client/src/project/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,12 @@ class View extends Component {
const projectData =
await this.projectCoordinator.fetchProject(this.props.client, pathComponents.projectPathWithNamespace);
this.fetchBranches();
if (!this.autostart && !this.customBranch) {
const defaultBranch = projectData.all?.default_branch;
this.projectCoordinator.fetchCommits({ branch: defaultBranch });
if (projectData && !this.autostart && !this.customBranch) {
const defaultBranch = projectData?.all?.default_branch;
const commitOptions = defaultBranch ?
{ branch: defaultBranch } :
{};
this.projectCoordinator.fetchCommits(commitOptions);
}

return projectData;
Expand All @@ -239,8 +242,8 @@ class View extends Component {
async fetchGraphStatus() { return this.projectCoordinator.fetchGraphStatus(this.props.client); }
saveProjectLastNode(nodeData) { this.projectCoordinator.saveProjectLastNode(nodeData); }

async fetchMigrationCheck() {
return this.projectCoordinator.fetchMigrationCheck(this.props.client);
async fetchMigrationCheck(gitUrl, defaultBranch) {
return this.projectCoordinator.fetchMigrationCheck(this.props.client, gitUrl, defaultBranch);
}
async fetchReadmeCommits() {
return this.projectCoordinator.fetchReadmeCommits(this.props.client);
Expand All @@ -257,25 +260,23 @@ class View extends Component {
if (pathComponents.projectPathWithNamespace)
projectData = await this.fetchProject();

// TODO: check project version for anonymous users. Requires SwissDataScienceCenter/renku-python#2541
// if (projectData) {
// const gitUrl = projectData.all?.http_url_to_repo;
// const defaultBranch = projectData.all?.default_branch;
// const checkMigration = await this.props.client.checkMigration(gitUrl, defaultBranch);
// }

// Check the supported core versions
if (projectData && this.props.user.logged) {
this.checkGraphWebhook();
const migrationData = await this.fetchMigrationCheck();
if (projectData) {
// check project webhook
if (this.props.user.logged)
this.checkGraphWebhook();

// Check the supported core versions
const gitUrl = projectData.all?.http_url_to_repo;
const defaultBranch = projectData.all?.default_branch;
const migrationData = await this.fetchMigrationCheck(gitUrl, defaultBranch);
const projectVersion = migrationData.core_compatibility_status?.project_metadata_version;
await this.checkCoreAvailability(projectVersion);
this.fetchProjectDatasets();
}
}

migrateProject(params) {
this.projectCoordinator.migrateProject(this.props.client, params);
migrateProject(gitUrl, branch, options) {
this.projectCoordinator.migrateProject(this.props.client, gitUrl, branch, options);
}

redirectProjectWithNumericId(projectId) {
Expand Down Expand Up @@ -615,8 +616,8 @@ class View extends Component {
fetchBranches: () => {
return this.projectCoordinator.fetchBranches();
},
onMigrateProject: (params) => {
return this.migrateProject(params);
onMigrateProject: (gitUrl, branch, options) => {
return this.migrateProject(gitUrl, branch, options);
}
};

Expand Down
57 changes: 25 additions & 32 deletions client/src/project/Project.state.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,44 +188,37 @@ const ProjectAttributesMixin = {
};

const MigrationMixin = {
async fetchMigrationCheck(client) {
const url = this.get("metadata.httpUrl");
const cacheIdResponse = await client.getProjectIdFromService(url);
if (cacheIdResponse.data && cacheIdResponse.data.error !== undefined) {
this.set("migration.check.check_error", cacheIdResponse.data.error.reason);
return cacheIdResponse.data.error.reason;
async fetchMigrationCheck(client, gitUrl, defaultBranch = null) {
const migrationData = await client.checkMigration(gitUrl, defaultBranch);
if (migrationData && migrationData.error !== undefined) {
this.set("migration.check.check_error", migrationData.error.reason);
return migrationData.error.reason;
}
const cacheId = cacheIdResponse;
const migrationResponse = await client.performMigrationCheck(cacheId);
if (migrationResponse.data && migrationResponse.data.error !== undefined) {
this.set("migration.check.check_error", migrationResponse.data.error);
return migrationResponse.data.error;
}
const migrationData = migrationResponse.data.result;
this.set("migration.check", migrationData);
return migrationData;
this.set("migration.check", migrationData.result);
return migrationData.result;
},
migrateProject(client, params) {
async migrateProject(client, gitUrl, defaultBranch = null, options) {
if (this.get("migration.migration_status") === MigrationStatus.MIGRATING)
return;
this.set("migration.migration_status", MigrationStatus.MIGRATING);
const url = this.get("metadata.httpUrl");
client.getProjectIdFromService(url)
.then((projectId)=>{
client.performMigration(projectId, params)
.then((response)=>{
if (response.data.error) {
this.set("migration.migration_status", MigrationStatus.ERROR);
this.set("migration.migration_error", response.data.error);
}
else {
this.fetchMigrationCheck(client).then(response => {
this.set("migration.migration_status", MigrationStatus.FINISHED);
this.set("migration.migration_error", null);
});
}
});
const response = await client.migrateProject(gitUrl, defaultBranch, options);
if (response.error) {
this.setObject({
migration: {
migration_status: MigrationStatus.ERROR,
migration_error: response.error
}
});
}
else {
await this.fetchMigrationCheck(client, gitUrl, defaultBranch);
this.setObject({
migration: {
migration_status: MigrationStatus.FINISHED,
migration_error: { $set: null }
}
});
}
}
};

Expand Down
10 changes: 0 additions & 10 deletions client/src/project/Project.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,16 +327,6 @@ describe("rendering ProjectVersionStatus", () => {
user: { logged: true },
};

it("does not render if is user not logged in", () => {
const allProps = { ...props };
allProps.user.logged = false;
const component = TestRenderer.create(
<ProjectVersionStatus key="versionStatus" {...allProps} />
);
expect(component.toJSON()).toBe(null);
allProps.user.logged = true;
});

it("shows bouncer if loading", () => {
const allProps = { ...props };
allProps.loading = true;
Expand Down
10 changes: 6 additions & 4 deletions client/src/project/status/ProjectVersionStatus.present.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ import TemplateStatus from "./TemplateVersionStatus.present";
import { ACCESS_LEVELS } from "../../api-client";

function ProjectVersionStatusBody(props) {
const logged = props.user.logged;
const maintainer = props.metadata.accessLevel >= ACCESS_LEVELS.MAINTAINER;
const isLogged = props.user.logged;
const onMigrateProject = props.onMigrateProject;

if (!isLogged) return null;
const onMigrateProject = async (options) => {
return await props.onMigrateProject(props.metadata?.httpUrl, props.metadata?.defaultBranch, options);
};

return <Fragment>
<Card key="renkuLabUICompatibility" className="border-rk-light mb-4">
Expand All @@ -51,6 +51,7 @@ function ProjectVersionStatusBody(props) {
<RenkuVersionStatus
launchNotebookUrl={props.launchNotebookUrl}
loading={props.loading}
logged={logged}
maintainer={maintainer}
migration={props.migration}
onMigrateProject={onMigrateProject}
Expand All @@ -66,6 +67,7 @@ function ProjectVersionStatusBody(props) {
externalUrl={props.externalUrl}
launchNotebookUrl={props.launchNotebookUrl}
loading={props.loading}
logged={logged}
maintainer={maintainer}
migration={props.migration}
onMigrateProject={onMigrateProject} />
Expand Down

0 comments on commit d5d4931

Please sign in to comment.