Skip to content

Latest commit

 

History

History
197 lines (163 loc) · 5.56 KB

05-db.md

File metadata and controls

197 lines (163 loc) · 5.56 KB

In this session, we will build a FlatFileSystem in a separate file.

The FlatFileSystem is a simple file system that allows only one level of hierarchy. There can only be folders under the root object, and only files inside folders.

Prerequisites

We will use ex.DynamoDb in this session. This library uses a localhost docker image of Amazon DynamoDb and requires the Docker daemon to be running. You can verify it with:

docker ps

If it is not running, please start the Daemon.

If you don't have Docker installed, please install Docker or OrbStack.

Instructions

  1. Create a test file backend/flat-file-system.test.w with the following content:
bring expect;
bring "./flat-file-system.w" as f;

let fs = new f.FlatFileSystem();

test "get and create folders" {
  expect.equal([], fs.listFolders());
  fs.createFolder("a");
  expect.equal(["a"], fs.listFolders());
}

test "get and create file" {
  fs.createFolder("d1");
  fs.createFile("d1", "f1", "Hello Wing");
  expect.equal(["f1"], fs.listFiles("d1"));
  expect.equal("Hello Wing", fs.getFile("d1", "f1"));
}
  1. As you notice, the above file expects backend/flat-file-system.w to exist. Let's create it with the skeleton API the test expects:
bring ex;
bring cloud;
bring util;

pub class FlatFileSystem {
  new() { }
  pub inflight createFolder(name: str) {  }
  pub inflight listFolders(): Array<str> {  }
  pub inflight createFile(folder: str, name: str, content: str){  }
  pub inflight listFiles(folder: str): Array<str> { }
  pub inflight getFile(folder: str, name: str): str { }
}
  1. Try running the test to see the result:
wing test backend/flat-file-system.test.w
  1. There are infinite options on how to implement this API. We will use ex.DynamoDb for file and directory names and cloud.Bucket for blob storage. Let's first create these two members in the class:
pub class FlatFileSystem {
  db: ex.DynamodbTable;
  blobStore: cloud.Bucket;

Notice that the compiler error indicates that these fields are not instantiated.


  1. Let's resolve the compilation errors by implementing the constructor:
  new() {
    this.db = new ex.DynamodbTable(
      attributeDefinitions: {
        "pk": "S",
      },
      hashKey: "pk",
      name: "filesystem",
    );   
    this.blobStore = new cloud.Bucket(); 
  }
  1. Let's also add implementations for createFile & createFolder, leaving you listFolders, listFiles & getFile unimplemented. There is also a private scan method included, which will come in handy. Here is backend/flat-file-system.w:
bring ex;
bring cloud;
bring util;

pub class FlatFileSystem {
  db: ex.DynamodbTable;
  blobStore: cloud.Bucket;
  new() {
    this.db = new ex.DynamodbTable(
      attributeDefinitions: {
        "pk": "S",
      },
      hashKey: "pk",
      name: "filesystem",
    );   
    this.blobStore = new cloud.Bucket(); 
  }

  pub inflight createFolder(name: str){
    this.db.putItem(
      item: {
        pk: "dir:{name}",
        name: name
    });
  }
  
  pub inflight createFile(folder: str, name: str, content: str){
    let key = util.sha256(content);
    this.blobStore.put(key, content);
    this.db.putItem(
      item: {
        pk: "file:folder-{folder}:name-{name}",
        name: name,
        key: key
    });
  }

  // helper function
  inflight scan(prefix: str): Array<str> {
    let response = this.db.scan(
      filterExpression: "begins_with(pk, :prefix)",
      expressionAttributeValues: { 
        ":prefix": prefix 
      }
    );
    let result = MutArray<str>[];
    for j in response.items {
      result.push(j.get("name").asStr());
    }
    return result.copy();
  }

  pub inflight listFolders(): Array<str> {
    // TODO
  }

  pub inflight listFiles(folder: str): Array<str> {
    // TODO
  }

  pub inflight getFile(folder: str, name: str): str {
    // TODO
  }
  
}
  1. To understand how to implement these functions, you can run wing run backend/flat-file-system.test.w to start the simulator. If you run the tests from the simulator, you can observe the changes in DynamoDb and Bucket state before and after. (Keep in mind that every test starts with a reset of state.)

Note: The first time you run wing run backend/flat-file-system.test.w, the amazon/dynamodb-local:2.0.0 images will be pulled. The Error: No such object: amazon/dynamodb-local:2.0.0 doesn't indicate a problem, see winglang/wing#5116 and 👍 it.

  1. Implement the missing methods and make sure all tests pass.
Solution
    pub inflight listFolders(): Array<str> {
      return this.scan("dir");
    }
   
    pub inflight listFiles(folder: str): Array<str> {
      return this.scan("file:folder-{folder}");
    }
  
    pub inflight getFile(folder: str, name: str): str {
      let response = this.db.getItem(
        key: {
          pk: "file:folder-{folder}:name-{name}"
      });
      let key = response.item?.get("key")?.asStr() ?? "";
      return this.blobStore.get(key);
    }
  1. Bonus: If you have Terraform installed and AWS credentials set up, you can verify this module also works on AWS:
wing test backend/flat-file-system.test.w -t tf-aws

🚀 Great, we have a FlatFileSystem module ready to be included in our webapp! 🚀