1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.adservices.service.js; 18 19 import static java.util.Arrays.asList; 20 21 import android.adservices.common.AdSelectionSignals; 22 23 import com.google.common.collect.ImmutableList; 24 25 import org.json.JSONException; 26 import org.json.JSONObject; 27 28 import java.util.List; 29 import java.util.Map; 30 import java.util.stream.Collectors; 31 32 /** Represent an argument to supply to an JS script. */ 33 public abstract class JSScriptArgument { 34 protected final String mName; 35 JSScriptArgument(String name)36 protected JSScriptArgument(String name) { 37 mName = name; 38 } 39 40 /** 41 * @return an argument with the given {@code name} and the given string value {@code value} 42 */ stringArg(String name, String value)43 public static JSScriptStringArgument stringArg(String name, String value) { 44 return new JSScriptStringArgument(name, value); 45 } 46 47 /** 48 * @return a JS object with the given {@code name} and value obtained parsing the given {@code 49 * value}. 50 * @throws JSONException if {@code value} doesn't represent a valid JSON object 51 */ jsonArg(String name, String value)52 public static JSScriptJsonArgument jsonArg(String name, String value) throws JSONException { 53 // Creating the JSONObject just to parse value and cause a JSONException if invalid. 54 new JSONObject(value); 55 return new JSScriptJsonArgument(name, value); 56 } 57 58 /** 59 * @return a JS object with the given {@code name} and value obtained parsing the given {@code 60 * value}. 61 * @throws JSONException if {@code value} doesn't represent a valid JSON object 62 */ jsonArg(String name, AdSelectionSignals value)63 public static JSScriptJsonArgument jsonArg(String name, AdSelectionSignals value) 64 throws JSONException { 65 // TODO(b/238849930) Merge this validation with AdSelectionSignals validation 66 new JSONObject(value.toString()); 67 return new JSScriptJsonArgument(name, value.toString()); 68 } 69 70 /** 71 * @return a JS object with the given {@code name} and value obtained parsing the given map 72 * {@code value}. 73 * @throws JSONException if {@code value} doesn't represent a valid JSON object 74 */ stringMapToRecordArg(String name, Map<String, String> stringMap)75 public static JSScriptArgument stringMapToRecordArg(String name, Map<String, String> stringMap) 76 throws JSONException { 77 ImmutableList.Builder<JSScriptArgument> mapArg = ImmutableList.builder(); 78 for (Map.Entry<String, String> signal : stringMap.entrySet()) { 79 mapArg.add(jsonArg(signal.getKey(), signal.getValue())); 80 } 81 return recordArg(name, mapArg.build()); 82 } 83 84 /** 85 * @return a JS array argument with the given {@code name} initialized with the values specified 86 * with {@code items}. 87 */ arrayArg( String name, T... items)88 public static <T extends JSScriptArgument> JSScriptArrayArgument<T> arrayArg( 89 String name, T... items) { 90 return new JSScriptArrayArgument<>(name, asList(items)); 91 } 92 93 /** 94 * @return a JS array argument with the given {@code name} initialized with the values specified 95 * with {@code items}. 96 */ arrayArg( String name, List<T> items)97 public static <T extends JSScriptArgument> JSScriptArrayArgument<T> arrayArg( 98 String name, List<T> items) { 99 return new JSScriptArrayArgument<>(name, items); 100 } 101 102 /** 103 * @return a JS array argument with the given {@code name} initialized with the values specified 104 * with {@code items}. 105 */ stringArrayArg( String name, List<String> items)106 public static JSScriptArrayArgument<JSScriptStringArgument> stringArrayArg( 107 String name, List<String> items) { 108 return new JSScriptArrayArgument<>( 109 name, 110 items.stream().map(str -> stringArg("ignored", str)).collect(Collectors.toList())); 111 } 112 113 /** 114 * @return a JS object with the given {@code name} and {@code fields} as fields values. 115 */ recordArg(String name, JSScriptArgument... fields)116 public static JSScriptRecordArgument recordArg(String name, JSScriptArgument... fields) { 117 return new JSScriptRecordArgument(name, asList(fields)); 118 } 119 120 /** 121 * @return a JS object with the given {@code name} and {@code fields} as fields values. 122 */ recordArg(String name, List<JSScriptArgument> fields)123 public static JSScriptRecordArgument recordArg(String name, List<JSScriptArgument> fields) { 124 return new JSScriptRecordArgument(name, fields); 125 } 126 127 /** 128 * @return a numeric variable with the given {@code name} and {@code value}. 129 */ numericArg(String name, T value)130 public static <T extends Number> JSScriptNumericArgument<T> numericArg(String name, T value) { 131 return new JSScriptNumericArgument<>(name, value); 132 } 133 134 /** 135 * @return the JS code to use to initialize the variable. 136 */ variableDeclaration()137 public String variableDeclaration() { 138 return String.format("const %s = %s;", name(), initializationValue()); 139 } 140 141 /** 142 * @return name of the argument as referred in the call to the auction script function. 143 */ name()144 public String name() { 145 return mName; 146 } 147 148 /** 149 * @return the JS code to use to initialize the newly declared variable. 150 */ initializationValue()151 abstract String initializationValue(); 152 } 153