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.JSONArray; 26 import org.json.JSONException; 27 import org.json.JSONObject; 28 29 import java.util.List; 30 import java.util.Map; 31 import java.util.stream.Collectors; 32 33 /** Represent an argument to supply to an JS script. */ 34 public abstract class JSScriptArgument { 35 protected final String mName; 36 JSScriptArgument(String name)37 protected JSScriptArgument(String name) { 38 mName = name; 39 } 40 41 /** 42 * @return an argument with the given {@code name} and the given string value {@code value} 43 */ stringArg(String name, String value)44 public static JSScriptStringArgument stringArg(String name, String value) { 45 return new JSScriptStringArgument(name, value); 46 } 47 48 /** 49 * @return a JS object with the given {@code name} and value obtained parsing the given {@code 50 * value}. 51 * @throws JSONException if {@code value} doesn't represent a valid JSON object 52 */ jsonArg(String name, String value)53 public static JSScriptJsonArgument jsonArg(String name, String value) throws JSONException { 54 // Creating the JSONObject just to parse value and cause a JSONException if invalid. 55 new JSONObject(value); 56 return new JSScriptJsonArgument(name, value); 57 } 58 59 /** 60 * @return a JS array object with the given {@code name} and value obtained parsing the given 61 * {@code value}. 62 * @throws JSONException if {@code value} doesn't represent a valid JSON array object 63 */ jsonArrayArg(String name, String value)64 public static JSScriptJsonArrayArgument jsonArrayArg(String name, String value) 65 throws JSONException { 66 new JSONArray(value); 67 return new JSScriptJsonArrayArgument(name, value); 68 } 69 70 /** 71 * @return a JS object with the given {@code name} and value obtained parsing the given {@code 72 * value}. 73 * @throws JSONException if {@code value} doesn't represent a valid JSON object 74 */ jsonArg(String name, AdSelectionSignals value)75 public static JSScriptJsonArgument jsonArg(String name, AdSelectionSignals value) 76 throws JSONException { 77 // TODO(b/238849930) Merge this validation with AdSelectionSignals validation 78 new JSONObject(value.toString()); 79 return new JSScriptJsonArgument(name, value.toString()); 80 } 81 82 /** 83 * @return a JS object with the given {@code name} and value obtained parsing the given map 84 * {@code value}. 85 * @throws JSONException if {@code value} doesn't represent a valid JSON object 86 */ stringMapToRecordArg(String name, Map<String, String> stringMap)87 public static JSScriptArgument stringMapToRecordArg(String name, Map<String, String> stringMap) 88 throws JSONException { 89 ImmutableList.Builder<JSScriptArgument> mapArg = ImmutableList.builder(); 90 for (Map.Entry<String, String> signal : stringMap.entrySet()) { 91 mapArg.add(jsonArg(signal.getKey(), signal.getValue())); 92 } 93 return recordArg(name, mapArg.build()); 94 } 95 96 /** 97 * @return a JS array argument with the given {@code name} initialized with the values specified 98 * with {@code items}. 99 */ arrayArg( String name, T... items)100 public static <T extends JSScriptArgument> JSScriptArrayArgument<T> arrayArg( 101 String name, T... items) { 102 return new JSScriptArrayArgument<>(name, asList(items)); 103 } 104 105 /** 106 * @return a JS array argument with the given {@code name} initialized with the values specified 107 * with {@code items}. 108 */ arrayArg( String name, List<T> items)109 public static <T extends JSScriptArgument> JSScriptArrayArgument<T> arrayArg( 110 String name, List<T> items) { 111 return new JSScriptArrayArgument<>(name, items); 112 } 113 114 /** 115 * @return a JS array argument with the given {@code name} initialized with the values specified 116 * with {@code items}. 117 */ stringArrayArg( String name, List<String> items)118 public static JSScriptArrayArgument<JSScriptStringArgument> stringArrayArg( 119 String name, List<String> items) { 120 return new JSScriptArrayArgument<>( 121 name, 122 items.stream().map(str -> stringArg("ignored", str)).collect(Collectors.toList())); 123 } 124 125 /** 126 * @return a JS array argument with the given {@code name} initialized with the values specified 127 * with {@code items} 128 */ 129 public static <T extends Number> numericArrayArg( String name, List<T> items)130 JSScriptArrayArgument<JSScriptNumericArgument<T>> numericArrayArg( 131 String name, List<T> items) { 132 return new JSScriptArrayArgument<>( 133 name, 134 items.stream() 135 .map(num -> new JSScriptNumericArgument<>("ignored", num)) 136 .collect(Collectors.toList())); 137 } 138 139 /** 140 * @return a JS object with the given {@code name} and {@code fields} as fields values. 141 */ recordArg(String name, JSScriptArgument... fields)142 public static JSScriptRecordArgument recordArg(String name, JSScriptArgument... fields) { 143 return new JSScriptRecordArgument(name, asList(fields)); 144 } 145 146 /** 147 * @return a JS object with the given {@code name} and {@code fields} as fields values. 148 */ recordArg(String name, List<JSScriptArgument> fields)149 public static JSScriptRecordArgument recordArg(String name, List<JSScriptArgument> fields) { 150 return new JSScriptRecordArgument(name, fields); 151 } 152 153 /** 154 * @return a numeric variable with the given {@code name} and {@code value}. 155 */ numericArg(String name, T value)156 public static <T extends Number> JSScriptNumericArgument<T> numericArg(String name, T value) { 157 return new JSScriptNumericArgument<>(name, value); 158 } 159 160 /** 161 * @return the JS code to use to initialize the variable. 162 */ variableDeclaration()163 public String variableDeclaration() { 164 return String.format("const %s = %s;", name(), initializationValue()); 165 } 166 167 /** 168 * @return name of the argument as referred in the call to the auction script function. 169 */ name()170 public String name() { 171 return mName; 172 } 173 174 /** 175 * @return the JS code to use to initialize the newly declared variable. 176 */ initializationValue()177 abstract String initializationValue(); 178 } 179