A simple project of getting Mars Rover images from NASA API and displaying them in JSON format I am learning to program and I have created this simple project in Python where I extract Mars Rover images for a given date from the NASA API:
https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?earth_date=2021-05-28&amp;camera=NAVCAM&amp;api_key=DEMO_KEY
and display all the images for the last 10 days from the current date in JSON format. The keys are the dates and their values are a list of all the images taken by Mars Rover on that date.
I stored all the images in a file as a cache to avoid exceeding the rate limit to the API (although a local file is not a great choice of caching, but I didn't want to use any libraries for that).
I have got all the output as desired and the project is working fine. However, I am not sure if my design and approach are acceptable, like following Object-Oriented, SOLID, and DRY principles, or if it's really clumsy.
I would like to have someone review my code, and give me honest feedback on my code, whether this is of the standard that interviewers look for.
This is the Github link to the project and if you wish to run the code for yourself, feel free to do so and follow the instructions in the READMe file in the project.
https://github.com/Vivek-Parupudi/nasaApiImages
Thanks in advance!
Sample output (included only a single date for the sake of simplicity):
{
&quot;2021-05-19&quot;: [
    &quot;https://mars.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/03123/opgs/edr/ncam/NLB_674737022EDR_F0880366NCAM00253M_.JPG&quot;,
    &quot;https://mars.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/03123/opgs/edr/ncam/NLB_674736991EDR_F0880366NCAM00253M_.JPG&quot;,
    &quot;https://mars.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/03123/opgs/edr/ncam/NLB_674736907EDR_F0880366NCAM00253M_.JPG&quot; ]

}
Here are a few things I found:
General practises

Using packages is great. But if you are just going to have one class/file in each package, then you probably don't need it. You could just have all your files in the challenge directory.

If you really needed to use packages, storing a data file in the package is not a great idea. Packages are meant for code. So your cache.json would probably be more suitable elsewhere. You could just have the cache file inside the challenge directory, or create a data directory inside challenge to contain it.

Providing a requirements.txt file is great. It is an easy way for someone to download you code and just be able to download its dependencies.

Files should end with an empty line. See this question.


Code style

If your docstrings are just one line, its fine to have have them as such:

def my_function():
  '''Some docs about my_function'''
  ...

However, if your docstring is longer than one line, its better to write it as:
def my_function():
  '''This is a oneliner description about my_function

  This is further information about it

  Foo

  Bar
  '''
  ...

It seems you are using this latter way of writing docs for you classes, but not for you methods. See NasaClient#generate_responde_data. For further information about docstrings, you can refer to PEP-257.
For further information regarding style, in python there is PEP-8, a general guide about it.
pathlib
Since python 3.4, we have pathlib available in the standard library, which makes the handling of paths more pythonic.
For example, you could change:
import os
absolute_path = os.path.dirname(os.path.abspath(__file__))
file_path = absolute_path + '/cache.json'
...
with open(FileCache.file_path, 'r') as file_reader:
...

in FileCache, with:
from pathlib import Path
absolute_path = Path(__file__).resolve().parent
file_path = absolute_path / 'cache.json'
...
with FileCache.file_path.open() as file_reader:
...

The usage of this library also gets rid of the os dependency if you are only using it for os.path
Handling exceptions / errors
In HttpClient, if the responde status code is not OK, you are returning a string containg an exception message.
That is probably not the best of ideas. Being able to tell, dinamycally, whether a returned value is as expected or not by examining the contents of a string is (which you actually don't even do in your code) is expensive and unnecessary.
You could either use None, or throw an actual exception:
...
def get_response(url):
  response = requests.get(url)
  if response.status_code == 200:
    return response
  else:
    return None
  # If you want to get fancy, you could use the ternary operator here:
  # return response if response.status_code == 200 else None
...
#Then when using get_response:
my_response = get_response()
if my_reponse:
  do_something(my_response)
else:
  handle_not_OK_reponse()

using exceptions:
class NotOKResponseException(Exception):
  pass
...
class HttpClient:
  def get_response(url):
    response = requests.get(url)
    if response.status_code == 200:
      return response
    else:
      raise NotOKResponseException()
...
#Then when using get_response:
try:
  my_response = get_response()
  do_something(my_response)
except NotOkResponseException:
  handle_not_OK_reponse()

Code design (Edit)
Regarding the comment:

The gateway method to actually run the code was 'print_response()' which should actually print the response but it also calls the NasaClient to generate the response. Is that a good design?

That is not the best of designs.
After reading what the OutputGenerator#print_response method does, its probably best to name it something like retrieve_printable_data, which indicates its behaviour more accurately.
Also, it does not seem right to me that the NasaClient stores the data in the cache file for the OutputGenerator to read it. There are two reasons I can currently think of:

NasaClient is already responsible for reading and writing the cache using FileCache. Therefore, to isolate this responsibility, just have NasaClient#generate_response_data return the actual data. That way, the cache is transparent to OutputGenerator.

OutputGenerator is just using the FileCache given to it through its constructor. Now, if you wished to change the cache to an in-memory cache, that would make the logic even more complex, as now whoever is constructing the instances not only has to know that OutputGenerator and NasaClient need a FileCache, but also that they need to have the same instance.