import axios from "axios"; //import {toBase64} from "@/utils/generalUtils" class RESTClient { /** * The RESTClient is a singleton class that handles the connection and data exchange from the back-end * REST API. */ constructor() { if (RESTClient._instance) { return RESTClient._instance; } RESTClient._instance = this; this.baseURL = process.env.VUE_APP_API_ENDPOINT; this.headers = { Accept: "application/json", "Content-Type": "application/json", "Cache-Control": "no-cache", }; this.headers["X-Client-Id"] = process.env.VUE_APP_CLIENT_ID; } /* USERS: all methods below related to handling user authentication */ /** * Method to add authorisation to the headers. * @param jwt - the user's json web token * @return {JSON} - headers with authorisation field added */ auth_headers(jwt) { let headers = JSON.parse(JSON.stringify(this.headers)); headers["Authorization"] = "Bearer " + jwt; return headers; } /** * Method to log in the user * @param username - name of the user * @param password - password of the user * @return {Promise} - the response of the server */ async login(username, password) { const endpoint = "/users/sign_in"; const body = { user: { login: username, password: password, }, }; const request = { method: "post", baseURL: this.baseURL + endpoint, data: body, headers: this.headers, }; let response = await this.executeQuery(request); return response.data; } /** * Logout the user from the back, expiring the current jwt. * @param {String} jwt - the user token to expire. * @returns {Promise} */ async logout(jwt) { const request = { method: "delete", baseURL: this.baseURL + "/users/sign_out", headers: this.auth_headers(jwt), }; let response = await this.executeQuery(request); return response.data; } /** * Method to create a new user * @param {Object} userLogin - the user account to create * @returns {Promise} response - server response */ async createAccount(userLogin) { const request = { method: "post", baseURL: this.baseURL + "/users", data: { user: userLogin }, headers: this.headers, }; let response = await this.executeQuery(request); return response.data; } /** * Validate the account given the corresponding token * @param {String} token - the account token to validate * @returns {Promise} */ async confirmAccount(token) { const request = { method: "get", baseURL: this.baseURL + "/users/confirmation?confirmation_token=" + token, headers: this.headers, }; let response = await this.executeQuery(request); return response.data; } /** * Method to send a reset password link to the given email address * @param {String} email to send the link to * @returns {Promise} */ async requestResetPwd(email) { const request = { method: "post", baseURL: this.baseURL + "/users/password", headers: this.headers, data: { user: { email: email }, }, }; let response = await this.executeQuery(request); return response.data; } /** * Resend the validation link for a given user * @param {Object} user - contains the email of the user. * @returns {Promise} */ async resendConfirmation(user) { const request = { method: "post", baseURL: this.baseURL + "/users/confirmation", headers: this.headers, data: { user: user }, }; let response = await this.executeQuery(request); return response.data; } /** * Reset the password of the given user * @param {Object} user - contains the new pwd, repeated pwd and token. * @returns {Promise} */ async resetPassword(user) { const request = { method: "put", baseURL: this.baseURL + "/users/password", headers: this.headers, data: { user: user }, }; let response = await this.executeQuery(request); return response.data; } /** * Changes the password of the logged in user * @param {String} jwt - the user token * @param {Object} user - contains the current, new and repeated new password * @returns {Promise} */ async resetPasswordWithoutToken(jwt, user) { const request = { method: "put", baseURL: this.baseURL + "/users/", headers: this.auth_headers(jwt), data: { user: user }, }; let response = await this.executeQuery(request); return response.data; } /** * Verify the validity of the given password * @param {String} password - the password to test * @returns {Promise} */ async verifyPassword(password) { let headers = JSON.parse(JSON.stringify(this.headers)); const request = { method: "post", baseURL: this.baseURL + "/users/check_password", headers: headers, data: { password: password }, }; let response = await this.executeQuery(request); return response.data; } /** * Get the current user data * @param jwt * @returns {Promise} */ async getUser(jwt) { const request = { method: "get", baseURL: this.baseURL + "/users/edit", headers: this.auth_headers(jwt), }; let response = await this.executeQuery(request); return response.data; } /** * Get the current user data * @param jwt * @param id * @returns {Promise} */ async getPublicUser(jwt, id) { const request = { method: "get", baseURL: this.baseURL + `/user_admin/${id}`, headers: this.auth_headers(jwt), }; let response = await this.executeQuery(request); return response.data; } /** * Get all users list * @param jwt * @returns {Promise} */ async getUsersList(jwt) { const request = { method: "get", baseURL: this.baseURL + "/user_admin/", headers: this.auth_headers(jwt), }; let response = await this.executeQuery(request); return response.data; } /** * Edit the current logged in user profile * @param {Object} newUser - the new values for the logged in user * @param {String} jwt - JWT of the logged in user * @returns {Promise} */ async editUser(newUser, jwt) { const request = { method: "put", baseURL: this.baseURL + "/users", headers: this.auth_headers(jwt), data: { user: newUser }, }; let response = await this.executeQuery(request); return response.data; } /** * Edit the current logged in user profile * @param {Object} newUser - the new values for the logged in user * @param {String} jwt - JWT of the logged in user * @returns {Promise} */ async editPublicUser(newUser, jwt) { const request = { method: "put", baseURL: this.baseURL + `/user_admin/${newUser.id}`, headers: this.auth_headers(jwt), data: { user: newUser }, }; let response = await this.executeQuery(request); return response.data; } /** * Delete the user * @param userID * @param {String} jwt - JWT of the logged in user * @returns {Promise} */ async deletePublicUser(userID, jwt) { const request = { method: "delete", baseURL: this.baseURL + `/user_admin/${userID}`, headers: this.auth_headers(jwt), }; let response = await this.executeQuery(request); return response.data; } /** * Verify that the given JWT is still valid * @param {String} jwt - the token to validate * @returns {Promise} */ async validateToken(jwt) { const request = { method: "get", baseURL: this.baseURL + "/users/valid", headers: this.auth_headers(jwt), }; let response = await this.executeQuery(request); return response.data; } /* EDITORS METHODS */ /** * Post the given object to the API to create the corresponding record. * @param record * @param {String} jwt - JWT of the logged in user * @returns {Promise} */ async createRecord(record, jwt) { const request = { method: "post", baseURL: this.baseURL + "/fairsharing_records", headers: this.auth_headers(jwt), data: { fairsharing_record: record }, }; let response = await this.executeQuery(request); return response.data; } /** * Update the given record * @param {Object} record - the record to update containing the ID to target, the new values * and the user token * @returns {Promise} */ async updateRecord(record) { const request = { method: "put", baseURL: this.baseURL + "/fairsharing_records/" + record.id, headers: this.auth_headers(record.token), data: { fairsharing_record: record.record }, }; let response = await this.executeQuery(request); return response.data; } /** * Determine if a user has permission to edit this record. * @param {Integer} recordID - ID for the relevant FairsharingRecord. * @param {String} userToken - JWT of the logged in user * @returns {Promise} */ async canEdit(recordID, userToken) { const request = { method: "get", baseURL: this.baseURL + "/fairsharing_records/can_edit/" + recordID, headers: this.auth_headers(userToken), }; let response = await this.executeQuery(request); return response.data; } /** * Attempt to create a MaintenanceRequest for a user for a FairsharingRecord. * @param {Integer} recordID - ID for the relevant FairsharingRecord. * @param {String} userToken - JWT of the logged in user * @returns {Promise} */ async claimRecord(recordID, userToken) { const request = { method: "post", baseURL: this.baseURL + "/maintenance_requests", headers: this.auth_headers(userToken), data: { maintenance_request: { fairsharing_record_id: recordID } }, }; let response = await this.executeQuery(request); return response.data; } /** * Determine if a user has permission to create a MaintenanceRequest for a FairsharingRecord. * @param {Integer} recordID - ID for the relevant FairsharingRecord. * @param {String} userToken - JWT of the logged in user * @returns {Promise} */ async canClaim(recordID, userToken) { const request = { method: "get", baseURL: this.baseURL + "/maintenance_requests/existing/" + recordID, headers: this.auth_headers(userToken), }; let response = await this.executeQuery(request); return response.data; } /** * Remove an existing maintainer from a FairsharingRecord. * @param {Integer} recordID - ID for the relevant FairsharingRecord. * @param {String} userToken - JWT of the logged in user * @returns {Promise} */ // TODO: Coverage steadfastly refuses to see this even though it is mocked and called // in a test (see Record.spec.js). /* istanbul ignore next */ async removeMaintainer(recordID, userToken) { const request = { method: "post", baseURL: this.baseURL + "/maintenance_requests/remove", headers: this.auth_headers(userToken), data: { maintenance_request: { fairsharing_record_id: recordID } }, }; let response = await this.executeQuery(request); return response.data; } /** * Add or remove an watcher to/from a FairsharingRecord. * @param {Integer} recordID - ID for the relevant FairsharingRecord. * @param {String} operation - 'add' or 'remove'. * @param {String} userToken - JWT of the logged in user * @returns {Promise} */ // TODO: Mocking this in a test (5c40b02ba3d78a49a088ab6dd5145c9ea10ea24b) // caused it to no longer be covered! /* istanbul ignore next */ async changeWatcher(recordID, operation, userToken) { const request = { method: "post", baseURL: this.baseURL + "/fairsharing_records/watch", headers: this.auth_headers(userToken), data: { record_id: recordID, operation: operation, }, }; let response = await this.executeQuery(request); return response.data; } /** * Attempt to create a RecordReview for a user for a FairsharingRecord. * @param {Integer} recordID - ID for the relevant FairsharingRecord. * @param {String} userToken - JWT of the logged in user * @returns {Promise} */ async reviewRecord(recordID, userToken) { const request = { method: "post", baseURL: this.baseURL + "/record_reviews", headers: this.auth_headers(userToken), data: { record_review: { fairsharing_record_id: recordID } }, }; let response = await this.executeQuery(request); return response.data; } /* USER DEFINED TAGS */ /** * Create a new user defined tag in the database for users to tag their records. * @param {String} term - the string value of the term * @param {String} token - the user JWT * @returns {Promise} */ async createNewUserDefinedTag(term, token) { const request = { method: "post", baseURL: this.baseURL + "/user_defined_tags", headers: this.auth_headers(token), data: { user_defined_tag: { label: term } }, }; let response = await this.executeQuery(request); return response.data; } /* LICENCES */ /** * Create new a licence link * @param {Object} licenceLink - the licence link to create * @param {String} token - the user token * @returns {Promise} */ async createLicenceLink(licenceLink, token) { let _client = this; const request = { method: "post", baseURL: _client.baseURL + "/licence_links", headers: this.auth_headers(token), data: { licence_link: licenceLink }, }; let response = await _client.executeQuery(request); return response.data; } /** * Delete the given licence * @param {Number} id - id of the licence link to delete * @param {String} token - the user token * @returns {Promise} */ async deleteLicenceLink(id, token) { let _client = this; const request = { method: "delete", baseURL: _client.baseURL + "/licence_links/" + id, headers: this.auth_headers(token), }; let response = await _client.executeQuery(request); return response.data; } /** * Update the licenceLink * @param {Object} licenceLink - the new values for the licence link * @param {String} token the user token * @returns {Promise} */ async updateLicenceLink(licenceLink, token) { let _client = this; const request = { method: "put", baseURL: _client.baseURL + "/licence_links/" + licenceLink.id, headers: this.auth_headers(token), data: { licence_link: licenceLink }, }; let response = await _client.executeQuery(request); return response.data; } /* PUBLICATIONS */ async createPublication(publication, token) { let _client = this; const request = { method: "post", baseURL: _client.baseURL + "/publications", headers: this.auth_headers(token), data: { publication: publication }, }; let response = await _client.executeQuery(request); return response.data; } async editPublication(publication, token) { let _client = this; const request = { method: "put", baseURL: _client.baseURL + "/publications/" + publication.id, headers: this.auth_headers(token), data: { publication: publication }, }; let response = await _client.executeQuery(request); return response.data; } /* RELATIONSHIPS BETWEEN RECORDS */ /** * Saves the relationships as an array of items containing a targetID, a sourceID and a labelID * @param {Object} options - the options to pass as {token: String, relations: Array} * @returns {Promise} */ async saveRelations(options) { if (options.relations.length > 0) { const request = { method: "put", baseURL: this.baseURL + "/fairsharing_records/" + options.target, headers: this.auth_headers(options.token), data: { fairsharing_record: { record_associations_attributes: options.relations, }, }, }; let response = await this.executeQuery(request); return response.data; } return {}; } /** * Deletes the relationships as an array of items containing a targetID, a sourceID and a labelID * @param {Object} options - the options to pass as {token: String, relations: Array} * @returns {Promise} */ async deleteRelations(options) { if (options.relations.length > 0) { const request = { method: "put", baseURL: this.baseURL + "/fairsharing_records/" + options.target, headers: this.auth_headers(options.token), data: { fairsharing_record: { record_associations_attributes: options.relations, }, }, }; let response = await this.executeQuery(request); return response.data; } return {}; } /* ORGANISATIONS AND GRANTS */ /** * Create a given organisation * @param {Object} organisation * @param {String} userToken - the user jwt * @returns {Promise} */ async createOrganisation(organisation, userToken) { let _client = this; const request = { method: "post", baseURL: _client.baseURL + "/organisations", headers: this.auth_headers(userToken), data: { organisation: organisation }, }; let response = await _client.executeQuery(request); return response.data; } /** * Edit a given organisation * @param {Object} organisation * @param {Number} id * @param {String} userToken - the user jwt * @returns {Promise} */ async editOrganisation(organisation, id, userToken) { let _client = this; const request = { method: "put", baseURL: _client.baseURL + "/organisations/" + id, headers: this.auth_headers(userToken), data: { organisation: organisation }, }; let response = await _client.executeQuery(request); return response.data; } /** * Delete the given organisation link * @param {Number} organisationId - the id of the link to remove * @param {String} userToken - the user jwt * @returns {Promise} */ async deleteOrganisation(organisationId, userToken) { let _client = this; const request = { method: "delete", baseURL: _client.baseURL + "/organisations/" + organisationId, headers: this.auth_headers(userToken), }; let response = await _client.executeQuery(request); return response.data; } /** * Create a given grant * @param {Object} grant * @param {String} userToken - the user jwt * @returns {Promise} */ async createGrant(grant, userToken) { let _client = this; const request = { method: "post", baseURL: _client.baseURL + "/grants", headers: this.auth_headers(userToken), data: { grant: grant }, }; let response = await _client.executeQuery(request); return response.data; } /** * Create a new link between an organisation, a record and an optional grant. * @param {Object} organisationLink - the organisation link to create * @param {String} userToken - the user jwt * @returns {Promise} */ async createOrganisationLink(organisationLink, userToken) { let _client = this; const request = { method: "post", baseURL: _client.baseURL + "/organisation_links", headers: this.auth_headers(userToken), data: { organisation_link: organisationLink }, }; let response = await _client.executeQuery(request); return response.data; } /** * Update the organisationLink given from linkID input with the given organisationLink * @param {Object} organisationLink - the new organisation link value * @param {Number} linkID - ID of the organisationLink to update * @param {String} userToken - the user jwt * @returns {Promise} */ async updateOrganisationLink(organisationLink, linkID, userToken) { let _client = this; const request = { method: "put", baseURL: _client.baseURL + "/organisation_links/" + linkID, headers: this.auth_headers(userToken), data: { organisation_link: organisationLink }, }; let response = await _client.executeQuery(request); return response.data; } /** * Delete the given organisation link * @param {Number} linkID - the id of the link to remove * @param {String} userToken - the user jwt * @returns {Promise} */ async deleteOrganisationLink(linkID, userToken) { let _client = this; const request = { method: "delete", baseURL: _client.baseURL + "/organisation_links/" + linkID, headers: this.auth_headers(userToken), }; let response = await _client.executeQuery(request); return response.data; } /** * Get the extra metadata fields for a RecordType * @param {String} type - name of the record type. * @param {String} userToken - the user jwt * @returns {Promise} */ async extraMetadataFields(type, userToken) { const request = { method: "post", baseURL: this.baseURL + "/fairsharing_records/metadata_fields", headers: this.auth_headers(userToken), data: { type: type }, }; let response = await this.executeQuery(request); return response.data; } /* METHODS FOR CURATION */ /** * Update the maintenanceRequest given the new status value * @param {Number} maintenanceRequest ID of the maintenanceRequest to update * @param {string} newStatus - new status to update * @param {String} userToken - the user jwt * @returns {Promise} */ async updateStatusMaintenanceRequest( maintenanceRequest, newStatus, userToken ) { let _client = this; const request = { method: "put", baseURL: _client.baseURL + "/maintenance_requests/" + maintenanceRequest, headers: this.auth_headers(userToken), data: { maintenance_request: { status: newStatus } }, }; let response = await _client.executeQuery(request); return response.data; } /** * Delete Record * @param {Number} id - id of the record link to delete * @param {String} token - the user token * @returns {Promise} */ async deleteRecord(id, token) { let _client = this; const request = { method: "delete", baseURL: _client.baseURL + "/fairsharing_records/" + id, headers: this.auth_headers(token), }; let response = await _client.executeQuery(request); return response.data; } /** * Get the list of allowed relation types for editing record's relationships. * @returns {Promise} */ /* EDITOR DATA */ async getRelationsTypes() { let _client = this; const request = { method: "get", baseURL: _client.baseURL + "/record_associations/allowed", }; let response = await _client.executeQuery(request); return response.data; } /** * Get the list of available profile types for a user. * @returns {Promise} */ async getProfileTypes() { const request = { method: "get", baseURL: this.baseURL + "/users/profile_types", headers: this.headers, }; let response = await this.executeQuery(request); return response.data; } /** * Get the list of available roles for a user. * @returns {Promise} * @param {String} token - the user token */ // Coverage steadfastly refuses to see this even though it is mocked and called // in a test (see EditPublicProfile.spec.js). /* istanbul ignore next */ async getUserRoles(userToken) { const request = { method: "get", baseURL: this.baseURL + "/user_roles", headers: this.auth_headers(userToken), }; let response = await this.executeQuery(request); return response.data; } /* SYSTEM MESSAGES */ /** * Update the given message * @param {Object} message - the message to update containing the ID to target and the new values * @param {String} userToken - the user jwt * @returns {Promise} */ async updateMessage(message, userToken) { const request = { method: "put", baseURL: this.baseURL + "/messages/" + message.id, headers: this.auth_headers(userToken), data: { message: message }, }; let response = await this.executeQuery(request); return response.data; } /** * Update the given message * @param {Object} message - the message to create * @param {String} userToken - the user jwt * @returns {Promise} */ async createMessage(message, userToken) { const request = { method: "post", baseURL: this.baseURL + "/messages/", headers: this.auth_headers(userToken), data: { message: message }, }; let response = await this.executeQuery(request); return response.data; } /** * Update the given message * @param {String} messageId - the ID of the message to delete * @param {String} userToken - the user jwt * @returns {Promise} */ async deleteMessage(messageId, userToken) { const request = { method: "delete", baseURL: this.baseURL + "/messages/" + messageId, headers: this.auth_headers(userToken), }; let response = await this.executeQuery(request); return response.data; } /* EXTRA METHODS */ /** * Trigger the given query with Axios * @param query * @returns {Promise<*>} */ async executeQuery(query) { try { return await axios(query); } catch (e) { return { data: { error: e } }; } } /** * Get records without DOIS * @param {String} token - the user token that needs to be a curator * @returns {Promise} */ async getRecordsWoDOIs(userToken) { let _client = this; const request = { method: "get", baseURL: _client.baseURL + "/files/no_dois", headers: this.auth_headers(userToken), }; let response = await _client.executeQuery(request); return response.data; } /** * Get count of records created by month * @param {String} token - the user token that needs to be a curator * @returns {Promise} */ async getRecordCreatedByMonth(userToken) { let _client = this; const request = { method: "get", baseURL: _client.baseURL + "/files/creation_dates", headers: this.auth_headers(userToken), }; let response = await _client.executeQuery(request); return response.data; } /** * Get count of edits by month * @param {String} token - the user token that needs to be a curator * @returns {Promise} */ async getEditByMonth(userToken) { let _client = this; const request = { method: "get", baseURL: _client.baseURL + "/files/edit_dates", headers: this.auth_headers(userToken), }; let response = await _client.executeQuery(request); return response.data; } /** * Get Zenodo call * @param {String} call * @param {String} userToken - the user jwt * @returns {Promise} */ async getZenodoSearch(text, userToken) { let _client = this; const request = { method: "post", baseURL: _client.baseURL + "/zenodo", headers: this.auth_headers(userToken), data: { doi: text }, }; let response = await _client.executeQuery(request); return response.data; } // This is covered in BaseFields.spec.js but doesn't show in coverage. /* istanbul ignore next */ async clearLogo(id, token) { let _client = this; const request = { method: "put", baseURL: _client.baseURL + "/fairsharing_records/" + id, headers: _client.auth_headers(token), data: { fairsharing_record: { logo: {}, }, }, }; let response = await _client.executeQuery(request); // here the data returned always should be either an array of images or one string of image return response.data; } /** * Get statistics data without users * @returns {Promise} */ async getStatisticsData() { let _client = this; const request = { method: "get", baseURL: _client.baseURL + "/homepage_stats", }; let response = await _client.executeQuery(request); return response.data; } /** * Get JSON+LD for homepage. * @returns {Promise} */ async getHomepageJsonld() { let _client = this; const request = { method: "get", baseURL: _client.baseURL + "/homepage_jsonld", }; let response = await _client.executeQuery(request); return response.data; } /** * Send the verification link for a given user * @param {Object} user - contains the email, uid, identifier of the user. * @returns {Promise} */ async sendOrcidVerification(user) { let _client = this; const request = { method: "post", baseURL: this.baseURL + "/users/auth/identify_user", headers: this.auth_headers(user), data: { user: user }, }; let response = await _client.executeQuery(request); return response.data; } /** * Save the advancedSearch results to the user * @param {Object} saveSearchObj - save search object having required parameters * @param {String} jwt - the user jwt * @returns {Promise} */ /* Ignoring because of 401 access denied in test case */ /* istanbul ignore next */ async saveSearch(saveSearchObj, jwt) { let _client = this; const request = { method: "post", baseURL: _client.baseURL + "/saved_searches", headers: this.auth_headers(jwt), data: { saved_search: saveSearchObj }, }; let response = await _client.executeQuery(request); return response.data; } /** * Delete the saved search * @param {Number} savedSearchId - the id of the savedSearch to remove * @param {String} userToken - the user jwt * @returns {Promise} */ /* Ignoring because of 401 access denied in test case */ /* istanbul ignore next */ async deleteSavedSearch(savedSearchId, jwt) { let _client = this; const request = { method: "delete", baseURL: _client.baseURL + "/saved_searches/" + savedSearchId, headers: this.auth_headers(jwt), }; let response = await _client.executeQuery(request); return response.data; } /** * Update the saved search * @param {Number} saveSearchId - save search id * @param {Object} saveSearchObj - save search object having required parameters * @param {String} jwt the user token * @returns {Promise} */ /* Ignoring because of 401 access denied in test case */ /* istanbul ignore next */ async updateSaveSearch(saveSearchId, saveSearchObj, jwt) { let _client = this; const request = { method: "put", baseURL: _client.baseURL + "/saved_searches/" + saveSearchId, headers: this.auth_headers(jwt), data: { saved_search: saveSearchObj }, }; let response = await _client.executeQuery(request); return response.data; } } export default RESTClient;