-
Notifications
You must be signed in to change notification settings - Fork 193
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
Implement feedback object hierarchy #4373
Changes from all commits
dad9c10
a0f3e76
bebadfc
da4dfdc
4fac0f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { v4 as uuidv4 } from 'uuid'; | ||
import { | ||
sendRequest, | ||
FlagFeedbackEvent, | ||
FeedbackTypeOptions, | ||
FLAG_FEEDBACK_EVENT_URL, | ||
} from '../feedbackApiUtils'; | ||
import client from '../client'; | ||
|
||
jest.mock('uuid', () => ({ v4: jest.fn(() => 'mocked-uuid') })); | ||
jest.mock('../client'); | ||
|
||
describe('FeedBackUtility Tests', () => { | ||
let flagFeedbackEvent; | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
beforeEach(() => { | ||
flagFeedbackEvent = new FlagFeedbackEvent({ | ||
context: { key: 'value' }, | ||
contentnode_id: uuidv4(), | ||
content_id: uuidv4(), | ||
target_topic_id: uuidv4(), | ||
feedback_type: FeedbackTypeOptions.flagged, | ||
feedback_reason: 'Inappropriate Language', | ||
}); | ||
client.post.mockRestore(); | ||
}); | ||
|
||
it('should generate data object without functions', () => { | ||
const dataObject = flagFeedbackEvent.getDataObject(); | ||
expect(dataObject.id).toEqual('mocked-uuid'); | ||
expect(dataObject.context).toEqual({ key: 'value' }); | ||
expect(dataObject.contentnode_id).toEqual('mocked-uuid'); | ||
expect(dataObject.content_id).toEqual('mocked-uuid'); | ||
|
||
expect(dataObject.getDataObject).toBeUndefined(); | ||
expect(dataObject.target_topic_id).toEqual('mocked-uuid'); | ||
expect(dataObject.feedback_type).toEqual(FeedbackTypeOptions.flagged); | ||
expect(dataObject.feedback_reason).toEqual('Inappropriate Language'); | ||
|
||
expect(dataObject.URL).toBeUndefined(); | ||
}); | ||
|
||
it('should throw an error when URL is not defined', () => { | ||
flagFeedbackEvent.URL = undefined; | ||
expect(() => flagFeedbackEvent.getUrl()).toThrowError( | ||
'URL is not defined for the FeedBack Object.' | ||
); | ||
}); | ||
|
||
it('should return the correct URL when URL is defined', () => { | ||
const result = flagFeedbackEvent.getUrl(); | ||
expect(result).toEqual(FLAG_FEEDBACK_EVENT_URL); | ||
}); | ||
|
||
it('should send a request using sendRequest function', async () => { | ||
client.post.mockResolvedValue(Promise.resolve({ data: 'Mocked API Response' })); | ||
|
||
const result = await sendRequest(flagFeedbackEvent); | ||
|
||
expect(result).toEqual('Mocked API Response'); | ||
expect(client.post).toHaveBeenCalledWith( | ||
FLAG_FEEDBACK_EVENT_URL, | ||
flagFeedbackEvent.getDataObject() | ||
); | ||
}); | ||
|
||
it('should handle errors when sending a request using sendRequest function', async () => { | ||
client.post.mockRejectedValue(new Error('Mocked API Error')); | ||
await expect(sendRequest(flagFeedbackEvent)).rejects.toThrowError('Mocked API Error'); | ||
expect(client.post).toHaveBeenCalledWith( | ||
FLAG_FEEDBACK_EVENT_URL, | ||
flagFeedbackEvent.getDataObject() | ||
); | ||
}); | ||
}); | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
// Helper functions and Utils for creating an API request to | ||
// Feedback mechanism endpoints | ||
import { v4 as uuidv4 } from 'uuid'; | ||
import client from './client'; | ||
import urls from 'shared/urls'; | ||
|
||
export const FeedbackTypeOptions = { | ||
imported: 'imported', | ||
rejected: 'rejected', | ||
previewed: 'previewed', | ||
showmore: 'showmore', | ||
ignored: 'ignored', | ||
flagged: 'flagged', | ||
}; | ||
|
||
// This is mock currently, fixed value of URL still to be decided | ||
// referencing the url by name | ||
export const FLAG_FEEDBACK_EVENT_URL = urls[`${'flagged'}_${'list'}`]; | ||
|
||
/** | ||
* @typedef {Object} BaseFeedbackParams | ||
* @property {Object} context - The context associated with the ObjectType. | ||
* @property {string} contentnode_id - Id for ContentNode that does not change. | ||
* @property {string} content_id - content_id of ContentNode wrt to ObjectType. | ||
*/ | ||
|
||
/** | ||
* Base class for feedback data objects. | ||
*/ | ||
class BaseFeedback { | ||
/** | ||
* Initializes a new BaseFeedback object. | ||
* | ||
* @class | ||
* @classdesc Represents a base feedback object with common properties and methods. | ||
* @param {BaseFeedbackParams} object | ||
*/ | ||
constructor({ context = {}, contentnode_id, content_id }) { | ||
this.id = uuidv4(); | ||
this.context = context; | ||
this.contentnode_id = contentnode_id; | ||
this.content_id = content_id; | ||
} | ||
|
||
// Creates a data object according to Backends expectation, | ||
// excluding functions and the "URL" property. | ||
getDataObject() { | ||
const dataObject = {}; | ||
for (const key in this) { | ||
if ( | ||
Object.prototype.hasOwnProperty.call(this, key) && | ||
typeof this[key] !== 'function' && | ||
key !== 'URL' | ||
) { | ||
dataObject[key] = this[key]; | ||
} | ||
} | ||
return dataObject; | ||
} | ||
|
||
// Return URL associated with the ObjectType | ||
getUrl() { | ||
if (this.defaultURL === null || this.URL === undefined) { | ||
throw new Error('URL is not defined for the FeedBack Object.'); | ||
} | ||
return this.URL; | ||
} | ||
} | ||
|
||
/** | ||
* Initializes a new BaseFeedbackEvent object. | ||
* | ||
* @param {Object} params - Parameters for initializing the object. | ||
* @param {string} params.user_id - The user_id associated with the feedback event. | ||
* @param {string} params.target_channel_id - The ID of the target channel for the feedback event. | ||
* @param {BaseFeedbackParams} baseFeedbackParams - Additional parameters inherited | ||
* from the base feedbackclass. | ||
*/ | ||
// eslint-disable-next-line no-unused-vars | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What was eslint complaining about here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently the BaseFeedbackEvent is not being used anywhere as the recommendations core classes like ReccommendationInteractionEvent and ReccommendationEvent class are still to be added! Hence its suggesting to remove the unused class. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this file is treated as an ES module, meaning if variables aren't used in the file then this would occur. Nothing is exported from this module, so doing that could perhaps resolve it? |
||
class BaseFeedbackEvent extends BaseFeedback { | ||
constructor({ user_id, target_channel_id, ...baseFeedbackParams }) { | ||
super(baseFeedbackParams); | ||
this.user_id = user_id; | ||
this.target_channel_id = target_channel_id; | ||
} | ||
} | ||
|
||
/** | ||
* Initializes a new BaseFeedbackInteractionEvent object. | ||
* @param {Object} params - Parameters for initializing the interaction event. | ||
* @param {string} params.feedback_type - The type of feedback for the interaction event. | ||
* @param {string} params.feedback_reason - The reason associated with the feedback. | ||
* @param {BaseFeedbackParams} baseFeedbackParams - Additional parameters inherited from the | ||
* base feedbackclass. | ||
*/ | ||
class BaseFeedbackInteractionEvent extends BaseFeedback { | ||
constructor({ feedback_type, feedback_reason, ...baseFeedbackParams }) { | ||
super(baseFeedbackParams); | ||
this.feedback_type = feedback_type; | ||
this.feedback_reason = feedback_reason; | ||
} | ||
} | ||
|
||
/** | ||
* Initializes a new BaseFlagFeedback object. | ||
* | ||
* @param {Object} params - Parameters for initializing the flag feedback. | ||
* @param {string} params.target_topic_id - The ID of the target topic associated with | ||
* the flag feedback. | ||
* @param {BaseFeedbackParams} baseFeedbackParams - Additional parameters inherited from the | ||
* base interaction event class. | ||
*/ | ||
class BaseFlagFeedback extends BaseFeedbackInteractionEvent { | ||
constructor({ target_topic_id, ...baseFeedbackParams }) { | ||
super({ ...baseFeedbackParams }); | ||
this.target_topic_id = target_topic_id; | ||
} | ||
} | ||
|
||
/** | ||
* Initializes a new FlagFeedbackEvent object. | ||
* | ||
* | ||
* @param {Object} params - Parameters for initializing the flag feedback event. | ||
* @param {string} params.target_topic_id - Id of the topic where the flagged content is located. | ||
* @param {Object} baseFeedbackParams - Additional parameters inherited from the | ||
* base flag feedback class. | ||
*/ | ||
export class FlagFeedbackEvent extends BaseFlagFeedback { | ||
constructor({ target_topic_id, ...baseFeedbackParams }) { | ||
super({ target_topic_id, ...baseFeedbackParams }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vkWeb mentioned that, it seems like target_topic_id can be ommited here. We can access the parent_id field of the contentNode to access the target_topic_id. |
||
this.URL = FLAG_FEEDBACK_EVENT_URL; | ||
} | ||
} | ||
|
||
/** | ||
* Sends a request using the provided feedback object. | ||
* | ||
* @function | ||
* | ||
* @param {BaseFeedback} feedbackObject - The feedback object to use for the request. | ||
* @throws {Error} Throws an error if the URL is not defined for the feedback object. | ||
* @returns {Promise<Object>} A promise that resolves to the response data from the API. | ||
*/ | ||
export async function sendRequest(feedbackObject) { | ||
try { | ||
const url = feedbackObject.getUrl(); | ||
const response = await client.post(url, feedbackObject.getDataObject()); | ||
console.log('API response:', response.data); | ||
return response.data; | ||
} catch (error) { | ||
console.error('Error:', error); | ||
throw error; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job on writing up some tests!