Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 52 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,54 @@
# Real Image Challenge 2016

In the cinema business, a feature film is usually provided to a regional distributor based on a contract for exhibition in a particular geographical territory.

Each authorization is specified by a combination of included and excluded regions. For example, a distributor might be authorzied in the following manner:
```
Permissions for DISTRIBUTOR1
INCLUDE: INDIA
INCLUDE: UNITEDSTATES
EXCLUDE: KARNATAKA-INDIA
EXCLUDE: CHENNAI-TAMILNADU-INDIA
```
This allows `DISTRIBUTOR1` to distribute in any city inside the United States and India, *except* cities in the state of Karnataka (in India) and the city of Chennai (in Tamil Nadu, India).

At this point, asking your program if `DISTRIBUTOR1` has permission to distribute in `CHICAGO-ILLINOIS-UNITEDSTATES` should get `YES` as the answer, and asking if distribution can happen in `CHENNAI-TAMILNADU-INDIA` should of course be `NO`. Asking if distribution is possible in `BANGALORE-KARNATAKA-INDIA` should also be `NO`, because the whole state of Karnataka has been excluded.

Sometimes, a distributor might split the work of distribution amount smaller sub-distiributors inside their authorized geographies. For instance, `DISTRIBUTOR1` might assign the following permissions to `DISTRIBUTOR2`:

```
Permissions for DISTRIBUTOR2 < DISTRIBUTOR1
INCLUDE: INDIA
EXCLUDE: TAMILNADU-INDIA
```
Now, `DISTRIBUTOR2` can distribute the movie anywhere in `INDIA`, except inside `TAMILNADU-INDIA` and `KARNATAKA-INDIA` - `DISTRIBUTOR2`'s permissions are always a subset of `DISTRIBUTOR1`'s permissions. It's impossible/invalid for `DISTRIBUTOR2` to have `INCLUDE: CHINA`, for example, because `DISTRIBUTOR1` isn't authorized to do that in the first place.

If `DISTRIBUTOR2` authorizes `DISTRIBUTOR3` to handle just the city of Hubli, Karnataka, India, for example:
```
Permissions for DISTRIBUTOR3 < DISTRIBUTOR2 < DISTRIBUTOR1
INCLUDE: HUBLI-KARNATAKA-INDIA
```
Again, `DISTRIBUTOR2` cannot authorize `DISTRIBUTOR3` with a region that they themselves do not have access to.

We've provided a CSV with the list of all countries, states and cities in the world that we know of - please use the data mentioned there for this program. *The codes you see there may be different from what you see here, so please always use the codes in the CSV*. This Readme is only an example.

Write a program in any language you want (If you're here from Gophercon, use Go :D) that does this. Feel free to make your own input and output format / command line tool / GUI / Webservice / whatever you want. Feel free to hold the dataset in whatever structure you want, but try not to use external databases - as far as possible stick to your langauage without bringing in MySQL/Postgres/MongoDB/Redis/Etc.

To submit a solution, fork this repo and send a Pull Request on Github.

For any questions or clarifications, raise an issue on this repo and we'll answer your questions as fast as we can.


Run application:
go run main.go


Prefix to be used while adding distributors:
ADMIN = "ADMIN"
DISTRIBUTOR_PREFIX = "D00"
SUBDISTRIBUTOR1_PREFIX = "SD01"
SUBDISTRIBUTOR2_PREFIX = "SD02"


1) API to add Distributors and Permissions:

curl --location --request POST 'http://localhost:3001/api/v1/addPermissions' \
--header 'Content-Type: application/json' \
--data-raw '{
"distributor":"ADMIN",
"subDistributor":"D00-01",
"include":["IN","BR-AL"],
"exclude":["MH-IN"]

}
'

curl --location --request POST 'http://localhost:3001/api/v1/addPermissions' \
--header 'Content-Type: application/json' \
--data-raw '{
"distributor":"D00-01",
"subDistributor":"SD01-01",
"include":["MH-IN","KA-IN"],
"exclude":["AL"]

}
'

curl --location --request POST 'http://localhost:3001/api/v1/addPermissions' \
--header 'Content-Type: application/json' \
--data-raw '{
"distributor":"SD01-01",
"subDistributor":"SD02-01",
"include":["KA-IN"],
"exclude":["ANEKL-KA-IN"]

}
'

2) Check if distributor has permission to the region:
curl --location --request GET 'http://localhost:3001/api/v1/isRegionAllowed?distributor=SD02-01&region=MH-IN'


3) Get distributor permission data
curl --location --request GET 'http://localhost:3001/api/v1/permissions?distributor=SD02-01'
14 changes: 14 additions & 0 deletions api/config/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package config

const (
COUNTRY int = iota
PROVINCE
CITY

CitiesFilePath = "../cities.csv"

ADMIN string = "ADMIN"
DISTRIBUTOR_PREFIX string = "D00"
SUBDISTRIBUTOR1_PREFIX string = "SD01"
SUBDISTRIBUTOR2_PREFIX string = "SD02"
)
3 changes: 3 additions & 0 deletions api/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module distributor-permissions

go 1.21.1
155 changes: 155 additions & 0 deletions api/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package main

import (
"distributor-permissions/config"
service "distributor-permissions/services"
"distributor-permissions/utils"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
)

var DR service.DistributorReceiver
var RR service.RegionReciever
var PR service.PermissionsReceiver

func AddDistributorData(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
payload := utils.RequestPayload{}
encode := json.NewDecoder(r.Body)
err := encode.Decode(&payload)
if err != nil {
log.Printf("Error in loading payload %s\n", err.Error())
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

if strings.TrimSpace(payload.Distributor) == "" || strings.TrimSpace(payload.SubDistributor) == "" {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}

switch strings.Split(payload.Distributor, "-")[0] {
case config.ADMIN:
if _, ok := PR.DistributorR.Distributors[payload.SubDistributor]; !ok {
PR.DistributorR.NewDistributor(payload.SubDistributor, payload.Distributor)
}
goto distributor
case config.DISTRIBUTOR_PREFIX:
if _, ok := PR.DistributorR.Distributors[payload.SubDistributor]; !ok {
err := PR.DistributorR.AddSubDistributor(payload.Distributor, payload.SubDistributor)
if err != nil {
log.Printf("Error in adding sub distributor %s\n", err.Error())
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
goto subDist
case config.SUBDISTRIBUTOR1_PREFIX:
if _, ok := PR.DistributorR.Distributors[payload.Distributor]; ok {
err := PR.DistributorR.AddSubDistributor(payload.Distributor, payload.SubDistributor)
if err != nil {
log.Printf("Error in adding sub distributor2 %s\n", err.Error())
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
goto subDist
case config.SUBDISTRIBUTOR2_PREFIX:
http.Error(w, "2nd SubDistributor cannot add data", http.StatusBadRequest)
return
default:
http.Error(w, "Incorrect distributor data", http.StatusBadRequest)
return

}

distributor:
{
data, err := PR.UpdateDistributorPermissions(payload.SubDistributor, payload.Include, payload.Exclude)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

fmt.Fprintf(w, "%+v", data)
return
}
subDist:
{
data, err := PR.UpdateSubDistributorPermissions(payload.Distributor, payload.SubDistributor, payload.Include, payload.Exclude)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

fmt.Fprintf(w, "%+v", data)
return
}

}

func HasRegionPermission(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
distributor := r.URL.Query().Get("distributor")
region := r.URL.Query().Get("region")
if strings.TrimSpace(distributor) == "" || strings.TrimSpace(region) == "" {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
data := PR.HasRegionPermission(distributor, region)
message := ""
if data {
message = "Distributor has " + region + " permission"
} else {
message = "Distributor doesn't have " + region + " permission"
}

fmt.Fprintf(w, "Allowed: %v\n Message: %s", data, message)
}

func GetDistributorData(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
distributor := r.URL.Query().Get("distributor")
data, ok := PR.DistributorPermissions[distributor]
message := ""
if ok {
message = "Distributor " + distributor + " record found"
} else {
message = "Distributor " + distributor + " record not found"
}
fmt.Fprintf(w, "Data: %+v\n Message: %s", data, message)
}

func main() {

err := RR.LoadAllRegions()
if err != nil {
log.Println("Error loading regions=>", err)
return
}

DR.Initilize()

PR.DistributorPermissions = make(map[string]utils.DistributorData)
PR.RegionR = &RR
PR.DistributorR = &DR

http.HandleFunc("/api/v1/addPermissions", AddDistributorData)
http.HandleFunc("/api/v1/isRegionAllowed", HasRegionPermission)
http.HandleFunc("/api/v1/permissions", GetDistributorData)

log.Println("Server listening to port 3001")
http.ListenAndServe(":3001", nil)
}
28 changes: 28 additions & 0 deletions api/services/distributors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package service

import (
"distributor-permissions/utils"
"errors"
)

type DistributorReceiver struct {
Distributors map[string]utils.Distributor
}

func (dr *DistributorReceiver) Initilize() {
dr.Distributors = make(map[string]utils.Distributor)
}

func (dr *DistributorReceiver) NewDistributor(name string, parentName string) {
dr.Distributors[name] = utils.Distributor{Name: name, ParentDistributor: parentName, Sub: make(map[string]bool)}
}

func (dr *DistributorReceiver) AddSubDistributor(dName string, subDName string) error {
if _, ok := dr.Distributors[dName]; ok {
dr.NewDistributor(subDName, dName)
dr.Distributors[dName].Sub[subDName] = true
return nil
}

return errors.New("distributor Not Found")
}
Loading